PluginProbe
Texty – SMS Notification for WordPress, WooCommerce, Dokan and more / 2.0.1
Texty – SMS Notification for WordPress, WooCommerce, Dokan and more v2.0.1
2.0.2 trunk 0.2 1.0 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 2.0.0 2.0.1
texty / includes / Api / Metrics.php

Metrics.php in Texty – SMS Notification for WordPress, WooCommerce, Dokan and more 2.0.1, at includes/Api/Metrics.php

256 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Texty\Api;
4
5 use DateTimeImmutable;
6 use Exception;
7 use Texty\Models\SmsStat;
8 use Texty\Models\SmsStatStore;
9 use WeDevs\WPKit\DataLayer\DataLayerFactory;
10 use WP_Error;
11 use WP_REST_Request;
12 use WP_REST_Response;
13 use WP_REST_Server;
14
15 /**
16 * Metrics REST Controller.
17 *
18 * @since 2.0.0
19 */
20 class Metrics extends Base {
21
22 /**
23 * Initialize
24 *
25 * @return void
26 * @since 2.0.0
27 */
28 public function __construct() {
29 $this->namespace = 'texty/v1';
30 $this->rest_base = 'metrics';
31 }
32
33 /**
34 * Registers the routes for the objects of the controller.
35 *
36 * @return void
37 * @since 2.0.0
38 */
39 public function register_routes() {
40 register_rest_route(
41 $this->namespace,
42 '/' . $this->rest_base,
43 [
44 [
45 'methods' => WP_REST_Server::READABLE,
46 'callback' => [ $this, 'get_metrics' ],
47 'permission_callback' => [ $this, 'admin_permissions_check' ],
48 'args' => [
49 'period' => [
50 'description' => __( 'The time range for metrics.', 'texty' ),
51 'type' => 'string',
52 'enum' => [ 'this_month', 'last_month', 'last_7_days', 'last_30_days', 'this_year' ],
53 'default' => 'this_month',
54 ],
55 ],
56 ],
57 ]
58 );
59 }
60
61 /**
62 * Get metrics data
63 *
64 * @param WP_REST_Request $request
65 *
66 * @return WP_REST_Response|WP_Error
67 * @since 2.0.0
68 */
69 public function get_metrics( $request ) {
70 try {
71 $store = DataLayerFactory::make_store( SmsStat::class );
72
73 if ( null === $store ) {
74 return new WP_Error( 'store_error', __( 'SMS data store not available.', 'texty' ), [ 'status' => 503 ] );
75 }
76
77 $period = $request->get_param( 'period' );
78 $range = $this->resolve_range( $period );
79
80 $items = $this->fetch_items( $range['start'], $range['end'] );
81
82 $sent = 0;
83 $delivered = 0;
84 $failed = 0;
85 foreach ( $items as $row ) {
86 ++$sent;
87 if ( isset( $row->status ) ) {
88 if ( 'sent' === $row->status ) {
89 ++$delivered;
90 } elseif ( 'failed' === $row->status ) {
91 ++$failed;
92 }
93 }
94 }
95
96 $delivery_rate = $sent > 0 ? round( ( $delivered / $sent ) * 100, 1 ) : 0;
97 $volume_chart = $this->build_volume_chart( $items, $range );
98
99 $gateway_name = texty()->settings()->gateway();
100
101 $response = [
102 'period' => $period,
103 'gateway_status' => $gateway_name ? true : false,
104 'gateway_name' => $gateway_name ? $gateway_name : '',
105 'sms_sent' => $sent,
106 'delivered' => $delivered,
107 'failed' => $failed,
108 'delivery_rate' => $delivery_rate,
109 'volume_chart' => $volume_chart,
110 ];
111
112 return rest_ensure_response( $response );
113 } catch ( Exception $e ) {
114 error_log( 'Texty Metrics Error: ' . $e->getMessage() . ' | ' . $e->getFile() . ':' . $e->getLine() );
115 return new WP_Error( 'metrics_error', __( 'Failed to load metrics data.', 'texty' ), [ 'status' => 500 ] );
116 }
117 }
118
119 /**
120 * Translate a period key into a date range and bucket granularity.
121 *
122 * @param string $period
123 *
124 * @return array{start:DateTimeImmutable,end:DateTimeImmutable,granularity:string,label:string}
125 * @since 2.0.0
126 */
127 private function resolve_range( $period ) {
128 $tz = wp_timezone();
129 $now = new DateTimeImmutable( 'now', $tz );
130
131 switch ( $period ) {
132 case 'last_month':
133 $start = $now->modify( 'first day of last month' )->setTime( 0, 0, 0 );
134 $end = $now->modify( 'last day of last month' )->setTime( 23, 59, 59 );
135 $label = __( 'Last Month', 'texty' );
136 $gran = 'day';
137 break;
138
139 case 'last_7_days':
140 $start = $now->modify( '-6 days' )->setTime( 0, 0, 0 );
141 $end = $now->setTime( 23, 59, 59 );
142 $label = __( 'Last 7 Days', 'texty' );
143 $gran = 'day';
144 break;
145
146 case 'last_30_days':
147 $start = $now->modify( '-29 days' )->setTime( 0, 0, 0 );
148 $end = $now->setTime( 23, 59, 59 );
149 $label = __( 'Last 30 Days', 'texty' );
150 $gran = 'day';
151 break;
152
153 case 'this_year':
154 $start = $now->modify( 'first day of January' )->setTime( 0, 0, 0 );
155 $end = $now->setTime( 23, 59, 59 );
156 $label = __( 'This Year', 'texty' );
157 $gran = 'month';
158 break;
159
160 case 'this_month':
161 default:
162 $start = $now->modify( 'first day of this month' )->setTime( 0, 0, 0 );
163 $end = $now->modify( 'last day of this month' )->setTime( 23, 59, 59 );
164 $label = __( 'This Month', 'texty' );
165 $gran = 'day';
166 break;
167 }
168
169 return [
170 'start' => $start,
171 'end' => $end,
172 'granularity' => $gran,
173 'label' => $label,
174 ];
175 }
176
177 /**
178 * Fetch SMS records in the given range.
179 *
180 * @param DateTimeImmutable $start
181 * @param DateTimeImmutable $end
182 *
183 * @return array
184 * @since 2.0.0
185 */
186 private function fetch_items( $start, $end ) {
187 $result = SmsStat::get_sent_sms_between_dates(
188 $start->format( 'Y-m-d' ),
189 $end->format( 'Y-m-d' )
190 );
191
192 if ( empty( $result['items'] ) || ! is_array( $result['items'] ) ) {
193 return [];
194 }
195
196 return $result['items'];
197 }
198
199 /**
200 * Build a series of buckets across the resolved range.
201 *
202 * Each bucket has:
203 * - `key` machine ID (Y-m-d for day, Y-m for month) — used by the chart's x-axis dataKey
204 * - `label` short label shown on the axis (Jan 1, Mar)
205 * - `date` long label shown in the tooltip (16 January 2025)
206 * - `count` number of SMS dispatched in that bucket
207 *
208 * @param array $items
209 * @param array $range
210 *
211 * @return array
212 * @since 2.0.0
213 */
214 private function build_volume_chart( $items, $range ) {
215 $tz = wp_timezone();
216 $start = $range['start'];
217 $end = $range['end'];
218 $is_month = 'month' === $range['granularity'];
219
220 $buckets = [];
221 $cursor = $start;
222 while ( $cursor <= $end ) {
223 $key = $is_month ? $cursor->format( 'Y-m' ) : $cursor->format( 'Y-m-d' );
224
225 $buckets[ $key ] = [
226 'key' => $key,
227 'label' => $is_month ? $cursor->format( 'M' ) : $cursor->format( 'M j' ),
228 'date' => $is_month ? $cursor->format( 'F Y' ) : $cursor->format( 'j F Y' ),
229 'count' => 0,
230 ];
231
232 $cursor = $cursor->modify( $is_month ? '+1 month' : '+1 day' );
233 }
234
235 foreach ( $items as $row ) {
236 if ( empty( $row->created_at ) ) {
237 continue;
238 }
239
240 try {
241 $created = new DateTimeImmutable( $row->created_at, $tz );
242 } catch ( Exception $e ) {
243 continue;
244 }
245
246 $key = $is_month ? $created->format( 'Y-m' ) : $created->format( 'Y-m-d' );
247
248 if ( isset( $buckets[ $key ] ) && isset( $row->status ) && 'sent' === $row->status ) {
249 ++$buckets[ $key ]['count'];
250 }
251 }
252
253 return array_values( $buckets );
254 }
255 }
256