1
|
|
|
# coding: utf8 |
2
|
|
|
|
3
|
|
|
""" |
4
|
|
|
This software is licensed under the Apache 2 license, quoted below. |
5
|
|
|
|
6
|
|
|
Copyright 2014 Crystalnix Limited |
7
|
|
|
|
8
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not |
9
|
|
|
use this file except in compliance with the License. You may obtain a copy of |
10
|
|
|
the License at |
11
|
|
|
|
12
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
13
|
|
|
|
14
|
|
|
Unless required by applicable law or agreed to in writing, software |
15
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
16
|
|
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
17
|
|
|
License for the specific language governing permissions and limitations under |
18
|
|
|
the License. |
19
|
|
|
""" |
20
|
|
|
|
21
|
|
|
import datetime |
22
|
|
|
|
23
|
|
|
from django.http import Http404 |
24
|
|
|
from django.conf import settings |
25
|
|
|
from django.utils import timezone |
26
|
|
|
|
27
|
|
|
from rest_framework import viewsets |
28
|
|
|
from rest_framework import mixins |
29
|
|
|
from rest_framework import pagination |
30
|
|
|
from rest_framework.views import APIView |
31
|
|
|
from rest_framework.response import Response |
32
|
|
|
|
33
|
|
|
|
34
|
|
|
from omaha.statistics import ( |
35
|
|
|
get_users_statistics_months, |
36
|
|
|
get_users_versions, |
37
|
|
|
get_channel_statistics, |
38
|
|
|
get_users_live_versions |
39
|
|
|
) |
40
|
|
|
from omaha.serializers import ( |
41
|
|
|
AppSerializer, |
42
|
|
|
DataSerializer, |
43
|
|
|
PlatformSerializer, |
44
|
|
|
ChannelSerializer, |
45
|
|
|
VersionSerializer, |
46
|
|
|
ActionSerializer, |
47
|
|
|
StatisticsMonthsSerializer, |
48
|
|
|
MonthRangeSerializer, |
49
|
|
|
MonthInputSerializer, |
50
|
|
|
ServerVersionSerializer, |
51
|
|
|
LiveStatisticsRangeSerializer, |
52
|
|
|
) |
53
|
|
|
from omaha.models import ( |
54
|
|
|
Application, |
55
|
|
|
Data, |
56
|
|
|
Platform, |
57
|
|
|
Channel, |
58
|
|
|
Version, |
59
|
|
|
Action, |
60
|
|
|
) |
61
|
|
|
from omaha.utils import get_month_range_from_dict |
62
|
|
|
|
63
|
|
|
class BaseView(mixins.ListModelMixin, mixins.CreateModelMixin, |
64
|
|
|
mixins.DestroyModelMixin, mixins.RetrieveModelMixin, |
65
|
|
|
viewsets.GenericViewSet): |
66
|
|
|
pass |
67
|
|
|
|
68
|
|
|
|
69
|
|
|
class StandardResultsSetPagination(pagination.PageNumberPagination): |
70
|
|
|
page_size = 10 |
71
|
|
|
page_size_query_param = 'page_size' |
72
|
|
|
max_page_size = 100 |
73
|
|
|
|
74
|
|
|
|
75
|
|
|
class AppViewSet(BaseView): |
76
|
|
|
""" |
77
|
|
|
API endpoint that allows applications to be viewed. |
78
|
|
|
|
79
|
|
|
## Applications Collection |
80
|
|
|
|
81
|
|
|
### List all Applications [GET] |
82
|
|
|
|
83
|
|
|
URL: `http://example.com/api/app/` |
84
|
|
|
|
85
|
|
|
Response: |
86
|
|
|
|
87
|
|
|
[ |
88
|
|
|
{ |
89
|
|
|
"id": "{8A76FC95-0086-4BCE-9517-DC09DDB5652F}", |
90
|
|
|
"name": "Chromium" |
91
|
|
|
}, |
92
|
|
|
{ |
93
|
|
|
"id": "{430FD4D0-B729-4F61-AA34-91526481799D}", |
94
|
|
|
"name": "Potato" |
95
|
|
|
} |
96
|
|
|
] |
97
|
|
|
|
98
|
|
|
|
99
|
|
|
### Create a Application [POST] |
100
|
|
|
|
101
|
|
|
URL: `http://example.com/api/app/` |
102
|
|
|
|
103
|
|
|
Headers: |
104
|
|
|
|
105
|
|
|
Content-Type: application/json |
106
|
|
|
|
107
|
|
|
Body: |
108
|
|
|
|
109
|
|
|
{ |
110
|
|
|
"id": "{8A76FC95-0086-4BCE-9517-DC09DDB5652F}", |
111
|
|
|
"name": "Chromium", |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
Response: |
115
|
|
|
|
116
|
|
|
HTTP 201 CREATED |
117
|
|
|
Content-Type: application/json |
118
|
|
|
|
119
|
|
|
{ |
120
|
|
|
"id": "{8A76FC95-0086-4BCE-9517-DC09DDB5652F}", |
121
|
|
|
"name": "Chromium", |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
## Application |
125
|
|
|
|
126
|
|
|
### Retrieve a Application [GET] |
127
|
|
|
|
128
|
|
|
URL: `http://example.com/api/app/[app_id]` |
129
|
|
|
|
130
|
|
|
Response: |
131
|
|
|
|
132
|
|
|
HTTP 201 CREATED |
133
|
|
|
Content-Type: application/json |
134
|
|
|
|
135
|
|
|
{ |
136
|
|
|
"id": "{8A76FC95-0086-4BCE-9517-DC09DDB5652F}", |
137
|
|
|
"name": "Chromium", |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
### Remove a Application [DELETE] |
141
|
|
|
|
142
|
|
|
URL: `http://example.com/api/app/[app_id]` |
143
|
|
|
|
144
|
|
|
Response: |
145
|
|
|
|
146
|
|
|
HTTP 204 NO CONTENT |
147
|
|
|
Content-Type: application/json |
148
|
|
|
""" |
149
|
|
|
queryset = Application.objects.all().order_by('-id') |
150
|
|
|
serializer_class = AppSerializer |
151
|
|
|
|
152
|
|
|
|
153
|
|
|
class DataViewSet(BaseView): |
154
|
|
|
queryset = Data.objects.all().order_by('-id') |
155
|
|
|
serializer_class = DataSerializer |
156
|
|
|
|
157
|
|
|
|
158
|
|
|
class PlatformViewSet(BaseView): |
159
|
|
|
queryset = Platform.objects.all().order_by('-id') |
160
|
|
|
serializer_class = PlatformSerializer |
161
|
|
|
|
162
|
|
|
|
163
|
|
|
class ChannelViewSet(viewsets.ModelViewSet): |
164
|
|
|
queryset = Channel.objects.all().order_by('-id') |
165
|
|
|
serializer_class = ChannelSerializer |
166
|
|
|
|
167
|
|
|
|
168
|
|
|
class VersionViewSet(BaseView): |
169
|
|
|
queryset = Version.objects.all().order_by('-id') |
170
|
|
|
serializer_class = VersionSerializer |
171
|
|
|
|
172
|
|
|
|
173
|
|
|
class ActionViewSet(BaseView): |
174
|
|
|
queryset = Action.objects.all().order_by('-id') |
175
|
|
|
serializer_class = ActionSerializer |
176
|
|
|
|
177
|
|
|
|
178
|
|
|
class StatisticsMonthsDetailView(APIView): |
179
|
|
|
def get_object(self, name): |
180
|
|
|
try: |
181
|
|
|
return Application.objects.get(name=name) |
182
|
|
|
except Application.DoesNotExist: |
183
|
|
|
raise Http404 |
184
|
|
|
|
185
|
|
|
def get(self, request, app_name, format=None): |
186
|
|
|
app = self.get_object(app_name) |
187
|
|
|
dates = MonthRangeSerializer(data=request.GET) |
188
|
|
|
dates.is_valid() |
189
|
|
|
|
190
|
|
|
start, end = get_month_range_from_dict(dates.validated_data) |
191
|
|
|
|
192
|
|
|
diapasons = [((start.month if year == start.year else 1, end.month if year == end.year else 12), year) |
193
|
|
|
for year in range(start.year, end.year+1)] |
194
|
|
|
|
195
|
|
|
win_data = dict(new=[], updates=[]) |
196
|
|
|
mac_data = dict(new=[], updates=[]) |
197
|
|
|
for diapason in diapasons: |
198
|
|
|
step = get_users_statistics_months(app_id=app.id, platform='win', year=diapason[1], start=diapason[0][0], end=diapason[0][1]) |
199
|
|
|
win_data['new'] += step['new'] |
200
|
|
|
win_data['updates'] += step['updates'] |
201
|
|
|
step = get_users_statistics_months(app_id=app.id, platform='mac', year=diapason[1], start=diapason[0][0], end=diapason[0][1]) |
202
|
|
|
mac_data['new'] += step['new'] |
203
|
|
|
mac_data['updates'] += step['updates'] |
204
|
|
|
|
205
|
|
|
serializer = StatisticsMonthsSerializer(dict(data=dict(win=win_data, mac=mac_data))) |
206
|
|
|
return Response(serializer.data) |
207
|
|
|
|
208
|
|
|
class StatisticsVersionsView(APIView): |
|
|
|
|
209
|
|
|
def get_object(self, name): |
210
|
|
|
try: |
211
|
|
|
return Application.objects.get(name=name) |
212
|
|
|
except Application.DoesNotExist: |
213
|
|
|
raise Http404 |
214
|
|
|
|
215
|
|
|
def get(self, request, app_name, format=None): |
216
|
|
|
now = timezone.now() |
217
|
|
|
app = self.get_object(app_name) |
218
|
|
|
|
219
|
|
|
date = MonthInputSerializer(data=request.GET) |
220
|
|
|
date.is_valid() |
221
|
|
|
date = date.validated_data.get('date', now) |
222
|
|
|
|
223
|
|
|
data = get_users_versions(app.id, date=date) |
224
|
|
|
serializer = StatisticsMonthsSerializer(dict(data=dict(data))) |
225
|
|
|
return Response(serializer.data) |
226
|
|
|
|
227
|
|
|
|
228
|
|
|
class StatisticsVersionsLiveView(APIView): |
229
|
|
|
def get_object(self, name): |
230
|
|
|
try: |
231
|
|
|
return Application.objects.get(name=name) |
232
|
|
|
except Application.DoesNotExist: |
233
|
|
|
raise Http404 |
234
|
|
|
|
235
|
|
|
def get(self, request, app_name, format=None): |
236
|
|
|
app = self.get_object(app_name) |
237
|
|
|
|
238
|
|
|
now = timezone.now() |
239
|
|
|
dates = LiveStatisticsRangeSerializer(data=request.GET) |
240
|
|
|
dates.is_valid() |
241
|
|
|
|
242
|
|
|
end = dates.validated_data.get('end', now) |
243
|
|
|
start = dates.validated_data.get('start', end - datetime.timedelta(hours=24)) |
244
|
|
|
|
245
|
|
|
data = get_users_live_versions(app.id, start, end, tz=request.session.get('django_timezone', 'UTC')) |
246
|
|
|
serializer = StatisticsMonthsSerializer(dict(data=dict(data))) |
247
|
|
|
return Response(serializer.data) |
248
|
|
|
|
249
|
|
|
|
250
|
|
|
class StatisticsChannelsView(APIView): |
|
|
|
|
251
|
|
|
def get_object(self, name): |
252
|
|
|
try: |
253
|
|
|
return Application.objects.get(name=name) |
254
|
|
|
except Application.DoesNotExist: |
255
|
|
|
raise Http404 |
256
|
|
|
|
257
|
|
|
def get(self, request, app_name, format=None): |
258
|
|
|
now = timezone.now() |
259
|
|
|
app = self.get_object(app_name) |
260
|
|
|
|
261
|
|
|
date = MonthInputSerializer(data=request.GET) |
262
|
|
|
date.is_valid() |
263
|
|
|
date = date.validated_data.get('date', now) |
264
|
|
|
|
265
|
|
|
data = get_channel_statistics(app.id, date=date) |
266
|
|
|
serializer = StatisticsMonthsSerializer(dict(data=dict(data))) |
267
|
|
|
return Response(serializer.data) |
268
|
|
|
|
269
|
|
|
|
270
|
|
|
class ServerVersionView(APIView): |
271
|
|
|
def get(self, request, format=None): |
272
|
|
|
version = settings.APP_VERSION |
273
|
|
|
serializer = ServerVersionSerializer(dict(version=version)) |
274
|
|
|
return Response(serializer.data) |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.