diff --git a/demo/index.html b/demo/index.html index 7a5cf6e..e70f08d 100644 --- a/demo/index.html +++ b/demo/index.html @@ -6,6 +6,16 @@ 新冠肺炎(2019-nCoV)疫情大屏 + + diff --git a/demo_proj/ncov/settings.py b/demo_proj/ncov/settings.py index 0dc2b0b..c44f307 100644 --- a/demo_proj/ncov/settings.py +++ b/demo_proj/ncov/settings.py @@ -10,6 +10,9 @@ https://docs.djangoproject.com/en/2.2/ref/settings/ """ +# insert project path here +import sys; sys.path.insert(0, '..') + import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) diff --git a/django_covid19/serializers.py b/django_covid19/serializers.py index 3e2736d..8d5e1ca 100644 --- a/django_covid19/serializers.py +++ b/django_covid19/serializers.py @@ -167,6 +167,13 @@ class Meta: exclude = ('id', 'dailyData') +class StateDailyListSerializer(serializers.ModelSerializer): + + class Meta: + model = models.State + fields = ['stateName', 'dailyData'] + + class StateDailySerializer(serializers.Serializer): state = serializers.CharField() diff --git a/django_covid19/urls.py b/django_covid19/urls.py index 7efad3d..fd3e665 100644 --- a/django_covid19/urls.py +++ b/django_covid19/urls.py @@ -27,6 +27,7 @@ path('countries//daily/', views.CountryDailyListView.as_view(), name='country-daily-list'), url(r'states/(?:(?Praw)/)?(?P[^/]+)/$', views.StateListView.as_view(), name='state-list'), + url(r'states/(?:(?Praw)/)?(?P[^/]+)/daily/$', views.StateListDailyListView.as_view(), name='state-list-daily-list'), url(r'states/(?:(?Praw)/)?(?P[^/]+)/(?P[A-Z]+)/$', views.StateRetrieveView.as_view(), name='state-detail'), url(r'states/(?:(?Praw)/)?(?P[^/]+)/(?P[A-Z]+)/daily/$', views.StateDailyListView.as_view(), name='state-daily-list'), url(r'states/(?:(?Praw)/)?(?P[^/]+)/(?P[^/]+)/$', views.StateRetrieveByNameView.as_view(), name='state-detail-by-name'), diff --git a/django_covid19/views.py b/django_covid19/views.py index 7e3a378..7889a30 100644 --- a/django_covid19/views.py +++ b/django_covid19/views.py @@ -320,37 +320,14 @@ def get(self, request, countryShortCode, state, raw=None): return Response(serializer.data) -class StateDailyListView(APIView): +class BaseDailyView(object): - """州按天返回列表""" - - def get_object(self, countryShortCode, state): - state = models.State.objects.filter( - countryShortCode=countryShortCode, state=state).first() - if state is None: - raise Http404 - return state - - @method_decorator(cache_page( - CACHE_PAGE_TIMEOUT, key_prefix='state-daily-list')) - def get(self, request, countryShortCode, state, raw=None): - inst = self.get_object(countryShortCode, state) - result = inst.dailyData - result = json.loads(result) - if raw == 'raw': - return Response(result) - data = [] - for r in result: - data.append(self.format(inst, r)) - serializer = serializers.StateDailySerializer(data, many=True) - return Response(serializer.data) - - def format(self, inst, data): + def format(self, countryShortCode, stateName, data): item = {} item['date'] = data['date'] item['state'] = data['state'] - item['stateName'] = inst.stateName - item['countryShortCode'] = inst.countryShortCode + item['stateName'] = stateName + item['countryShortCode'] = countryShortCode item['confirmedCount'] = data.get('positive') item['currentConfirmedCount'] = self.get_current_confirmed(data) @@ -376,7 +353,34 @@ def get_current_confirmed_incr(self, data): death = data['deathIncrease'] if data.get('deathIncrease') else 0 return positive - death -class StateDailyListByNameView(StateDailyListView): + +class StateDailyListView(APIView, BaseDailyView): + + """州按天返回列表""" + + def get_object(self, countryShortCode, state): + state = models.State.objects.filter( + countryShortCode=countryShortCode, state=state).first() + if state is None: + raise Http404 + return state + + @method_decorator(cache_page( + CACHE_PAGE_TIMEOUT, key_prefix='state-daily-list')) + def get(self, request, countryShortCode, state, raw=None): + inst = self.get_object(countryShortCode, state) + result = inst.dailyData + result = json.loads(result) + if raw == 'raw': + return Response(result) + stateName = inst.stateName + data = [] + for r in result: + data.append(self.format(countryShortCode, stateName, r)) + serializer = serializers.StateDailySerializer(data, many=True) + return Response(serializer.data) + +class StateDailyListByNameView(APIView, BaseDailyView): def get_object(self, countryShortCode, stateName): state = models.State.objects.filter( @@ -393,8 +397,54 @@ def get(self, request, countryShortCode, stateName, raw=None): result = json.loads(result) if raw == 'raw': return Response(result) + stateName = inst.stateName data = [] for r in result: - data.append(self.format(inst, r)) + data.append(self.format(countryShortCode, stateName, r)) serializer = serializers.StateDailySerializer(data, many=True) - return Response(serializer.data) \ No newline at end of file + return Response(serializer.data) + + +class StateListDailyListView(ListAPIView, BaseDailyView): + + serializer_class = serializers.StateDailyListSerializer + filter_class = filters.StateFilter + + def get_queryset(self): + countryShortCode = self.kwargs['countryShortCode'] + return models.State.objects.filter( + countryShortCode=countryShortCode).order_by('state') + + def list(self, request, *args, **kwargs): + countryShortCode = kwargs['countryShortCode'] + queryset = self.filter_queryset(self.get_queryset()) + + if kwargs.get('raw') == 'raw': + self.serializer_class = serializers.StateRawSerializer + + result = [] + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + for item in serializer.data: + stateName = item['stateName'] + dailyData = json.loads(item['dailyData']) + for daily in dailyData: + result.append( + self.format(countryShortCode, stateName, daily)) + return self.get_paginated_response(result) + + serializer = self.get_serializer(queryset, many=True) + for item in serializer.data: + stateName = item['stateName'] + dailyData = json.loads(item['dailyData']) + for daily in dailyData: + result.append( + self.format(countryShortCode, stateName, daily)) + return Response(result) + + @method_decorator(cache_page( + CACHE_PAGE_TIMEOUT, key_prefix='state-list-daily-list')) + def dispatch(self, *args, **kwargs): + return super(StateListDailyListView, self).dispatch(*args, **kwargs) + diff --git a/docs/README.md b/docs/README.md index ffae0a8..2af20d3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -783,6 +783,76 @@ http://111.231.75.86:8000/api/states/USA/?stateNames=Alaska,Alabama ] ``` + +#### 多个州的日统计 :id=state-USA-list-daily + +可获取全部州或某几个州的日统计数据列表; + +接口地址:/api/states/USA/\/daily/ + +原始数据:/api/states/raw/USA/\/daily/ + +请求方法:GET + +请求参数: + +参数 | 描述 +------------------- | ------- +stateNames | 州名,如:Alaska,Alabama;以逗号分割多个值;大小写敏感; +states | 州缩写,如:AK(Alaska),AL(Alabama);大小写敏感; + +示例链接: + +http://111.231.75.86:8000/api/states/USA/daily/ + +http://111.231.75.86:8000/api/states/USA/daily/?states=AK,AL + +http://111.231.75.86:8000/api/states/USA/daily/?stateNames=Alaska,Alabama + +返回结果: + +``` +[ + { + "date": 20200306, + "state": "AK", + "stateName": "Alaska", + "countryShortCode": "USA", + "confirmedCount": 0, + "currentConfirmedCount": 0, + "suspectedCount": 1, + "curedCount": null, + "deadCount": 0, + "currentConfirmedIncr": 0, + "confirmedIncr": null, + "suspectedIncr": null, + "curedIncr": null, + "deadIncr": null + }, + // (20200307 - 现在)AK 日统计 + ... + { + "date": 20200307, + "state": "AL", + "stateName": "Alabama", + "countryShortCode": "USA", + "confirmedCount": 0, + "currentConfirmedCount": 0, + "suspectedCount": null, + "curedCount": null, + "deadCount": null, + "currentConfirmedIncr": 0, + "confirmedIncr": null, + "suspectedIncr": null, + "curedIncr": null, + "deadIncr": null + }, + // (20200307 - 现在)AL 日统计 + ... + // 其他州的日统计 +``` + + #### 某州最新疫情 :id=state-USA-detail diff --git a/setup.cfg b/setup.cfg index 8304969..ed9d8d3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = django_covid19 -version = 0.4a0 +version = 0.4a1 description = An django app of the covid-19 API in countries around the world, provinces and cities in China, and states in the USA. long_description = file: README.md long-description-content-type = text/markdown