-4af2488e594185b8c43f316872dd79c2b123ba09R12">12
+from utils.error.errno_utils import AdministratorStatusCode, IsolationPointStatusCode
13
+
14
+
15
+WECHAT = settings.WECHAT
16
+
17
+
18
+@logit
19
+def admin_login(request):
20
+    phone = request.POST.get('phone', '')
21
+    password = request.POST.get('password', '')
22
+
23
+    try:
24
+        administrator = AdministratorInfo.objects.get(phone=phone, status=True)
25
+    except AdministratorInfo.DoesNotExist:
26
+        return response(AdministratorStatusCode.ADMINISTRATOR_NOT_FOUND)
27
+
28
+    if administrator.user_status == AdministratorInfo.DISABLED:
29
+        return response(AdministratorStatusCode.ADMINISTRATOR_NOT_ACTIVATED)
30
+    elif administrator.user_status == AdministratorInfo.DELETED:
31
+        return response(AdministratorStatusCode.ADMINISTRATOR_HAS_DELETED)
32
+
33
+    if not check_password(password, administrator.encryption):
34
+        return response(AdministratorStatusCode.ADMINISTRATOR_PASSWORD_ERROR)
35
+
36
+    try:
37
+        point = IsolationPointInfo.objects.get(point_id=administrator.point_id, status=True)
38
+    except IsolationPointInfo.DoesNotExist:
39
+        return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)
40
+
41
+    return response(200, 'Admin Login Success', '管理员登录成功', data={**administrator.data, **point.data})

+ 121 - 0
api/eqpt_views.py

@@ -0,0 +1,121 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from __future__ import division
4
+
5
+from django.db.models import Q
6
+from django_logit import logit
7
+from django_response import response
8
+from TimeConvert import TimeConvert as tc
9
+
10
+from equipment.models import (IsolationPointInfo, ThermometerEquipmentInfo, ThermometerMeasureInfo,
11
+                              ThermometerMeasureLogInfo)
12
+from utils.error.errno_utils import IsolationPointStatusCode, ThermometerEquipmentStatusCode
13
+
14
+
15
+@logit
16
+def eqpt_bind(request):
17
+    point_id = request.POST.get('point_id', '')
18
+    macid = request.POST.get('macid', '')
19
+    sn = request.POST.get('sn', '')
20
+
21
+    ThermometerEquipmentInfo.objects.update_or_create(macid=macid, defaults={
22
+        'point_id': point_id,
23
+        'sn': sn,
24
+    })
25
+
26
+    return response()
27
+
28
+
29
+@logit
30
+def eqpt_onoff(request):
31
+    macid = request.POST.get('macid', '')
32
+    active = request.POST.get('active', 0)
33
+
34
+    ThermometerEquipmentInfo.objects.update_or_create(macid=macid, defaults={
35
+        'active_status': active,
36
+        'active_at': tc.utc_datetime(),
37
+    })
38
+
39
+    return response()
40
+
41
+
42
+@logit
43
+def eqpt_list(request):
44
+    point_id = request.POST.get('point_id', '')
45
+    macid = request.POST.get('macid', '')
46
+
47
+    eqpts = ThermometerEquipmentInfo.objects.filter(point_id=point_id, status=True)
48
+    if macid:
49
+        eqpts = eqpts.filter(macid=macid)
50
+    eqpts = [eqpt.data for eqpt in eqpts]
51
+
52
+    total_num = len(eqpts)
53
+    active_num = len([1 for eqpt in eqpts if eqpt.get('active_status') == ThermometerEquipmentInfo.ONLINE])
54
+
55
+    return response(data={
56
+        'eqpts': eqpts,
57
+        'total_num': total_num,
58
+        'active_num': active_num,
59
+        'unactive_num': total_num - active_num,
60
+    })
61
+
62
+
63
+@logit
64
+def eqpt_result(request):
65
+    point_id = request.POST.get('point_id', '')
66
+    kw = request.POST.get('kw', '')
67
+
68
+    try:
69
+        point = IsolationPointInfo.objects.get(point_id=point_id, status=True)
70
+    except IsolationPointInfo.DoesNotExist:
71
+        return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)
72
+
73
+    logs = ThermometerMeasureInfo.objects.filter(point_id=point_id, point_measure_ymd=tc.local_string(format='%Y-%m-%d'), point_measure_window=point.point_measure_window, status=True).order_by('-pk')
74
+
75
+    eqpts = ThermometerEquipmentInfo.objects.filter(point_id=point_id, active_status=ThermometerEquipmentInfo.ONLINE, status=True)
76
+    if kw:
77
+        eqpts = eqpts.filter(Q(name__icontains=kw) | Q(phone__icontains=kw))
78
+    eqpts = [eqpt.data for eqpt in eqpts]
79
+
80
+    return response(data={
81
+        'eqpts': eqpts,
82
+    })
83
+
84
+
85
+@logit
86
+def upload_temperature(request):
87
+    macid = request.POST.get('macid', '')
88
+    name = request.POST.get('name', '')
89
+    sex = request.POST.get('sex', 0)
90
+    age = request.POST.get('age', 0)
91
+    phone = request.POST.get('phone', '')
92
+    temperature = request.POST.get('temperature', 0)
93
+
94
+    try:
95
+        eqpt = ThermometerEquipmentInfo.objects.get(macid=macid, status=True)
96
+    except ThermometerEquipmentInfo.DoesNotExist:
97
+        return response(ThermometerEquipmentStatusCode.THERMOMETER_EQUIPMENT_NOT_FOUND)
98
+
99
+    try:
100
+        point = IsolationPointInfo.objects.get(point_id=eqpt.point_id, status=True)
101
+    except IsolationPointInfo.DoesNotExist:
102
+        return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)
103
+
104
+    point_measure_ymd = tc.local_string(format='%Y-%m-%d')
105
+    point_measure_window = point.current_measure_window
106
+
107
+    eqpt.name = name
108
+    eqpt.sex = sex
109
+    eqpt.age = age
110
+    eqpt.phone = phone
111
+    eqpt.last_submit_at = tc.utc_datetime()
112
+    eqpt.save()
113
+
114
+    ThermometerMeasureLogInfo.objects.create(point_id=eqpt.point_id, macid=macid, temperature=temperature)
115
+
116
+    if point_measure_window:
117
+        ThermometerMeasureInfo.objects.update_or_create(point_id=eqpt.point_id, point_measure_ymd=point_measure_ymd, point_measure_window=point_measure_window, macid=macid, defaults={
118
+            'temperature': temperature,
119
+        })
120
+
121
+    return response()

+ 25 - 0
api/point_views.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from __future__ import division
4
+
5
+from django_logit import logit
6
+from django_response import response
7
+
8
+from equipment.models import IsolationPointInfo
9
+from utils.error.errno_utils import IsolationPointStatusCode
10
+
11
+
12
+@logit
13
+def measure_window(request):
14
+    point_id = request.POST.get('point_id', '')
15
+    point_measure_window = request.POST.get('point_measure_window', '')
16
+
17
+    try:
18
+        point = IsolationPointInfo.objects.get(point_id=point_id, status=True)
19
+    except IsolationPointInfo.DoesNotExist:
20
+        return response(IsolationPointStatusCode.ISOLATIONPOINT_NOT_FOUND)
21
+
22
+    point.point_measure_window = point_measure_window
23
+    point.save()
24
+
25
+    return response()

+ 20 - 1
api/urls.py

@@ -2,7 +2,7 @@
2 2
 
3 3
 from django.conf.urls import url
4 4
 
5
-from api import oauth_views
5
+from api import admin_views, eqpt_views, oauth_views, point_views
6 6
 
7 7
 
8 8
 urlpatterns = [
@@ -12,3 +12,22 @@ urlpatterns += [
12 12
     url(r'^3rd/or$', oauth_views.oauth_redirect, name='3rd_or'),
13 13
     url(r'^3rd/oauth_redirect$', oauth_views.oauth_redirect, name='3rd_oauth_redirect'),
14 14
 ]
15
+
16
+urlpatterns += [
17
+    url(r'^admin/login$', admin_views.admin_login, name='admin_login'),
18
+]
19
+
20
+urlpatterns += [
21
+    url(r'^point/measure_window$', point_views.measure_window, name='measure_window'),
22
+]
23
+
24
+urlpatterns += [
25
+    url(r'^eqpt/bind$', eqpt_views.eqpt_bind, name='eqpt_bind'),
26
+    url(r'^eqpt/onoff$', eqpt_views.eqpt_onoff, name='eqpt_onoff'),
27
+    url(r'^eqpt/list$', eqpt_views.eqpt_list, name='eqpt_list'),
28
+    url(r'^eqpt/result$', eqpt_views.eqpt_result, name='eqpt_result'),
29
+]
30
+
31
+urlpatterns += [
32
+    url(r'^upload/temperature$', eqpt_views.upload_temperature, name='upload_temperature'),
33
+]

+ 0 - 0
equipment/__init__.py


+ 31 - 0
equipment/admin.py

@@ -0,0 +1,31 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from equipment.models import (IsolationPointInfo, ThermometerEquipmentInfo, ThermometerMeasureInfo,
6
+                              ThermometerMeasureLogInfo)
7
+
8
+
9
+class IsolationPointInfoAdmin(admin.ModelAdmin):
10
+    list_display = ('point_id', 'point_name', 'point_measure_window', 'status', 'updated_at', 'created_at')
11
+
12
+
13
+class ThermometerEquipmentInfoAdmin(admin.ModelAdmin):
14
+    list_display = ('eqpt_id', 'point_id', 'macid', 'sn', 'active_status', 'active_at', 'name', 'sex', 'age', 'phone', 'remark', 'last_submit_at', 'status', 'updated_at', 'created_at')
15
+    list_filter = ('point_id', 'status')
16
+
17
+
18
+class ThermometerMeasureInfoAdmin(admin.ModelAdmin):
19
+    list_display = ('point_id', 'point_measure_ymd', 'point_measure_window', 'macid', 'sn', 'temperature', 'status', 'updated_at', 'created_at')
20
+    list_filter = ('point_id', 'status')
21
+
22
+
23
+class ThermometerMeasureLogInfoAdmin(admin.ModelAdmin):
24
+    list_display = ('point_id', 'macid', 'sn', 'temperature', 'status', 'updated_at', 'created_at')
25
+    list_filter = ('point_id', 'status')
26
+
27
+
28
+admin.site.register(IsolationPointInfo, IsolationPointInfoAdmin)
29
+admin.site.register(ThermometerEquipmentInfo, ThermometerEquipmentInfoAdmin)
30
+admin.site.register(ThermometerMeasureInfo, ThermometerMeasureInfoAdmin)
31
+admin.site.register(ThermometerMeasureLogInfo, ThermometerMeasureLogInfoAdmin)

+ 5 - 0
equipment/apps.py

@@ -0,0 +1,5 @@
1
+from django.apps import AppConfig
2
+
3
+
4
+class AccountConfig(AppConfig):
5
+    name = 'equipment'

+ 72 - 0
equipment/migrations/0001_initial.py

@@ -0,0 +1,72 @@
1
+# Generated by Django 3.2.4 on 2021-07-09 01:41
2
+
3
+from django.db import migrations, models
4
+import jsonfield.fields
5
+import shortuuidfield.fields
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    initial = True
11
+
12
+    dependencies = [
13
+    ]
14
+
15
+    operations = [
16
+        migrations.CreateModel(
17
+            name='IsolationPointInfo',
18
+            fields=[
19
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
+                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
21
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
22
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
23
+                ('point_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='隔离点唯一标识', max_length=22, null=True, unique=True)),
24
+                ('point_name', models.CharField(blank=True, help_text='隔离点名称', max_length=255, null=True, verbose_name='point_name')),
25
+                ('point_measure_window', jsonfield.fields.JSONField(blank=True, default=[], help_text='隔离点测温时间段', null=True, verbose_name='point_measure_window')),
26
+            ],
27
+            options={
28
+                'verbose_name': '隔离点信息',
29
+                'verbose_name_plural': '隔离点信息',
30
+            },
31
+        ),
32
+        migrations.CreateModel(
33
+            name='ThermometerEquipmentInfo',
34
+            fields=[
35
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
36
+                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
37
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
38
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
39
+                ('eqpt_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='设备唯一标识', max_length=22, null=True, unique=True)),
40
+                ('point_id', models.CharField(blank=True, db_index=True, help_text='隔离点唯一标识', max_length=32, null=True, verbose_name='point_id')),
41
+                ('macid', models.CharField(blank=True, help_text='设备号', max_length=255, null=True, verbose_name='macid')),
42
+                ('sn', models.CharField(blank=True, help_text='序列号', max_length=255, null=True, verbose_name='sn')),
43
+                ('active_status', models.IntegerField(choices=[(1, '已激活'), (0, '已离线')], default=0, help_text='激活状态', verbose_name='active_status')),
44
+                ('active_at', models.DateTimeField(blank=True, help_text='激活时间', null=True, verbose_name='active_at')),
45
+                ('name', models.CharField(blank=True, help_text='用户姓名', max_length=255, null=True, verbose_name='name')),
46
+                ('sex', models.IntegerField(choices=[(0, '未知'), (1, '男'), (2, '女')], default=0, help_text='用户性别', verbose_name='sex')),
47
+                ('age', models.IntegerField(default=0, help_text='用户年龄', verbose_name='age')),
48
+                ('phone', models.CharField(blank=True, db_index=True, help_text='用户电话', max_length=11, null=True, verbose_name='phone')),
49
+                ('remark', models.CharField(blank=True, help_text='备注', max_length=255, null=True, verbose_name='remark')),
50
+            ],
51
+            options={
52
+                'verbose_name': '测温设备信息',
53
+                'verbose_name_plural': '测温设备信息',
54
+            },
55
+        ),
56
+        migrations.CreateModel(
57
+            name='ThermometerMeasureInfo',
58
+            fields=[
59
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
60
+                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
61
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
62
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
63
+                ('macid', models.CharField(blank=True, help_text='设备号', max_length=255, null=True, verbose_name='macid')),
64
+                ('sn', models.CharField(blank=True, help_text='序列号', max_length=255, null=True, verbose_name='sn')),
65
+                ('temperature', models.FloatField(default=0, help_text='用户体温', verbose_name='temperature')),
66
+            ],
67
+            options={
68
+                'verbose_name': '测温记录信息',
69
+                'verbose_name_plural': '测温记录信息',
70
+            },
71
+        ),
72
+    ]

+ 18 - 0
equipment/migrations/0002_thermometermeasureinfo_point_id.py

@@ -0,0 +1,18 @@
1
+# Generated by Django 3.2.4 on 2021-07-09 04:54
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('equipment', '0001_initial'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='thermometermeasureinfo',
15
+            name='point_id',
16
+            field=models.CharField(blank=True, db_index=True, help_text='隔离点唯一标识', max_length=32, null=True, verbose_name='point_id'),
17
+        ),
18
+    ]

+ 43 - 0
equipment/migrations/0003_auto_20210709_1332.py

@@ -0,0 +1,43 @@
1
+# Generated by Django 3.2.4 on 2021-07-09 05:32
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('equipment', '0002_thermometermeasureinfo_point_id'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.CreateModel(
14
+            name='ThermometerMeasureLogInfo',
15
+            fields=[
16
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
17
+                ('status', models.BooleanField(default=True, help_text='Status', verbose_name='status')),
18
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
19
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
20
+                ('point_id', models.CharField(blank=True, db_index=True, help_text='隔离点唯一标识', max_length=32, null=True, verbose_name='point_id')),
21
+                ('macid', models.CharField(blank=True, help_text='设备号', max_length=255, null=True, verbose_name='macid')),
22
+                ('sn', models.CharField(blank=True, help_text='序列号', max_length=255, null=True, verbose_name='sn')),
23
+                ('temperature', models.FloatField(default=0, help_text='用户体温', verbose_name='temperature')),
24
+            ],
25
+            options={
26
+                'verbose_name': '测温记录信息',
27
+                'verbose_name_plural': '测温记录信息',
28
+            },
29
+        ),
30
+        migrations.AlterModelOptions(
31
+            name='thermometermeasureinfo',
32
+            options={'verbose_name': '测温信息', 'verbose_name_plural': '测温信息'},
33
+        ),
34
+        migrations.AddField(
35
+            model_name='thermometermeasureinfo',
36
+            name='point_measure_window',
37
+            field=models.CharField(blank=True, db_index=True, help_text='隔离点测温时间段', max_length=16, null=True, verbose_name='point_measure_window'),
38
+        ),
39
+        migrations.AlterUniqueTogether(
40
+            name='thermometermeasureinfo',
41
+            unique_together={('point_id', 'point_measure_window')},
42
+        ),
43
+    ]

+ 17 - 0
equipment/migrations/0004_alter_thermometermeasureinfo_unique_together.py

@@ -0,0 +1,17 @@
1
+# Generated by Django 3.2.4 on 2021-07-09 05:34
2
+
3
+from django.db import migrations
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('equipment', '0003_auto_20210709_1332'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AlterUniqueTogether(
14
+            name='thermometermeasureinfo',
15
+            unique_together={('point_id', 'point_measure_window', 'macid')},
16
+        ),
17
+    ]

+ 18 - 0
equipment/migrations/0005_thermometerequipmentinfo_last_submit_at.py

@@ -0,0 +1,18 @@
1
+# Generated by Django 3.2.4 on 2021-07-09 05:42
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('equipment', '0004_alter_thermometermeasureinfo_unique_together'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='thermometerequipmentinfo',
15
+            name='last_submit_at',
16
+            field=models.DateTimeField(blank=True, help_text='上一次上报时间', null=True, verbose_name='last_submit_at'),
17
+        ),
18
+    ]

+ 22 - 0
equipment/migrations/0006_auto_20210709_1346.py

@@ -0,0 +1,22 @@
1
+# Generated by Django 3.2.4 on 2021-07-09 05:46
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('equipment', '0005_thermometerequipmentinfo_last_submit_at'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='thermometermeasureinfo',
15
+            name='point_measure_ymd',
16
+            field=models.CharField(blank=True, db_index=True, help_text='隔离点测温日期', max_length=10, null=True, verbose_name='point_measure_ymd'),
17
+        ),
18
+        migrations.AlterUniqueTogether(
19
+            name='thermometermeasureinfo',
20
+            unique_together={('point_id', 'point_measure_ymd', 'point_measure_window', 'macid')},
21
+        ),
22
+    ]

+ 0 - 0
equipment/migrations/__init__.py


+ 157 - 0
equipment/models.py

@@ -0,0 +1,157 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import ugettext_lazy as _
5
+from django_models_ext import BaseModelMixin, SexModelMixin
6
+from jsonfield import JSONField
7
+from shortuuidfield import ShortUUIDField
8
+from TimeConvert import TimeConvert as tc
9
+
10
+
11
+class IsolationPointInfo(BaseModelMixin):
12
+    point_id = ShortUUIDField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True, unique=True)
13
+    point_name = models.CharField(_('point_name'), max_length=255, blank=True, null=True, help_text='隔离点名称')
14
+    # [{"start": "8:00", "end": "9:00"}, {"start": "12:00", "end": "14:00"}]
15
+    point_measure_window = JSONField(_('point_measure_window'), default=[], blank=True, null=True, help_text='隔离点测温时间段')
16
+
17
+    class Meta:
18
+        verbose_name = _('隔离点信息')
19
+        verbose_name_plural = _('隔离点信息')
20
+
21
+    def __unicode__(self):
22
+        return '%d' % self.pk
23
+
24
+    @property
25
+    def data(self):
26
+        return {
27
+            'point_id': self.point_id,
28
+            'point_name': self.point_name,
29
+            'point_measure_window': self.point_measure_window,
30
+        }
31
+
32
+    @property
33
+    def current_measure_window(self):
34
+        current_ymd = tc.local_string(format='%Y-%m-%d')
35
+        current_dt = tc.utc_datetime()
36
+        for window in self.point_measure_window:
37
+            start_t, end_t = window.get('start'), window.get('end')
38
+            start_dt = tc.string_to_utc_datetime(f'{current_ymd} {start_t}:00')
39
+            end_dt = tc.string_to_utc_datetime(f'{current_ymd} {end_t}:00')
40
+            if start_dt < current_dt < end_dt:
41
+                return f'{start_t}-{end_t}'
42
+        return ''
43
+
44
+
45
+class ThermometerEquipmentInfo(BaseModelMixin):
46
+    ONLINE = 1
47
+    OFFLINE = 0
48
+
49
+    ACTIVE_STATUE_TUPLE = (
50
+        (ONLINE, '已激活'),
51
+        (OFFLINE, '已离线'),
52
+    )
53
+
54
+    eqpt_id = ShortUUIDField(_('eqpt_id'), max_length=32, blank=True, null=True, help_text='设备唯一标识', db_index=True, unique=True)
55
+
56
+    point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
57
+
58
+    macid = models.CharField(_('macid'), max_length=255, blank=True, null=True, help_text='设备号')
59
+    sn = models.CharField(_('sn'), max_length=255, blank=True, null=True, help_text='序列号')
60
+
61
+    active_status = models.IntegerField(_('active_status'), choices=ACTIVE_STATUE_TUPLE, default=OFFLINE, help_text='激活状态')
62
+    active_at = models.DateTimeField(_('active_at'), blank=True, null=True, help_text=_('激活时间'))
63
+
64
+    # 用户基本信息
65
+    name = models.CharField(_('name'), max_length=255, blank=True, null=True, help_text='用户姓名')
66
+    sex = models.IntegerField(_('sex'), choices=SexModelMixin.SEX_TUPLE, default=SexModelMixin.UNKNOWN, help_text='用户性别')
67
+    age = models.IntegerField(_('age'), default=0, help_text='用户年龄')
68
+    phone = models.CharField(_('phone'), max_length=11, blank=True, null=True, help_text='用户电话', db_index=True)
69
+
70
+    remark = models.CharField(_('remark'), max_length=255, blank=True, null=True, help_text='备注')
71
+
72
+    last_submit_at = models.DateTimeField(_('last_submit_at'), blank=True, null=True, help_text=_('上一次上报时间'))
73
+
74
+    class Meta:
75
+        verbose_name = _('测温设备信息')
76
+        verbose_name_plural = _('测温设备信息')
77
+
78
+    def __unicode__(self):
79
+        return '%d' % self.pk
80
+
81
+    @property
82
+    def data(self):
83
+        return {
84
+            'eqpt_id': self.eqpt_id,
85
+            'point_id': self.point_id,
86
+            'macid': self.macid,
87
+            'sn': self.sn,
88
+            'active_status': self.active_status,
89
+            'active_status_str': dict(self.ACTIVE_STATUE_TUPLE).get(self.active_status, ''),
90
+            'active_at': tc.local_string(utc_dt=self.active_at),
91
+            'name': self.name or '',
92
+            'sex': self.sex,
93
+            'sex_str': dict(SexModelMixin.SEX_TUPLE).get(self.sex, ''),
94
+            'age': self.age or '',
95
+            'phone': self.phone or '',
96
+            'remark': self.remark or '',
97
+            'last_submit_at': tc.local_string(utc_dt=self.last_submit_at),
98
+            'created_at': tc.local_string(utc_dt=self.created_at),
99
+        }
100
+
101
+
102
+class ThermometerMeasureInfo(BaseModelMixin):
103
+    point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
104
+
105
+    point_measure_ymd = models.CharField(_('point_measure_ymd'), max_length=10, blank=True, null=True, help_text='隔离点测温日期', db_index=True)
106
+    point_measure_window = models.CharField(_('point_measure_window'), max_length=16, blank=True, null=True, help_text='隔离点测温时间段', db_index=True)
107
+
108
+    macid = models.CharField(_('macid'), max_length=255, blank=True, null=True, help_text='设备号')
109
+    sn = models.CharField(_('sn'), max_length=255, blank=True, null=True, help_text='序列号')
110
+
111
+    temperature = models.FloatField(_('temperature'), default=0, help_text='用户体温')
112
+
113
+    class Meta:
114
+        verbose_name = _('测温信息')
115
+        verbose_name_plural = _('测温信息')
116
+
117
+        unique_together = (
118
+            ('point_id', 'point_measure_ymd', 'point_measure_window', 'macid')
119
+        )
120
+
121
+    def __unicode__(self):
122
+        return '%d' % self.pk
123
+
124
+    @property
125
+    def data(self):
126
+        return {
127
+            'point_id': self.point_id,
128
+            'point_measure_window': self.point_measure_window,
129
+            'macid': self.macid,
130
+            'sn': self.sn,
131
+            'temperature': self.temperature,
132
+        }
133
+
134
+
135
+class ThermometerMeasureLogInfo(BaseModelMixin):
136
+    point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
137
+
138
+    macid = models.CharField(_('macid'), max_length=255, blank=True, null=True, help_text='设备号')
139
+    sn = models.CharField(_('sn'), max_length=255, blank=True, null=True, help_text='序列号')
140
+
141
+    temperature = models.FloatField(_('temperature'), default=0, help_text='用户体温')
142
+
143
+    class Meta:
144
+        verbose_name = _('测温记录信息')
145
+        verbose_name_plural = _('测温记录信息')
146
+
147
+    def __unicode__(self):
148
+        return '%d' % self.pk
149
+
150
+    @property
151
+    def data(self):
152
+        return {
153
+            'point_id': self.point_id,
154
+            'macid': self.macid,
155
+            'sn': self.sn,
156
+            'temperature': self.temperature,
157
+        }

+ 4 - 0
equipment/tests.py

@@ -0,0 +1,4 @@
1
+from django.test import TestCase
2
+
3
+
4
+# Create your tests here.

+ 4 - 0
equipment/views.py

@@ -0,0 +1,4 @@
1
+from django.shortcuts import render
2
+
3
+
4
+# Create your views here.

+ 9 - 7
thermometer/basemodels.py

@@ -5,21 +5,23 @@ from django.utils.translation import ugettext_lazy as _
5 5
 
6 6
 
7 7
 class BaseModelMixin(models.Model):
8
-    status = models.BooleanField(_(u'status'), default=True, help_text=_(u'状态'))
9
-    created_at = models.DateTimeField(_(u'created_at'), auto_now_add=True, editable=True, help_text=_(u'创建时间'))
10
-    updated_at = models.DateTimeField(_(u'updated_at'), auto_now=True, editable=True, help_text=_(u'更新时间'))
8
+    status = models.BooleanField(_('status'), default=True, help_text=_('状态'), db_index=True)
9
+    created_at = models.DateTimeField(_('created_at'), auto_now_add=True, editable=True, help_text=_('创建时间'))
10
+    updated_at = models.DateTimeField(_('updated_at'), auto_now=True, editable=True, help_text=_('更新时间'))
11 11
 
12 12
     class Meta:
13 13
         abstract = True
14 14
 
15 15
 
16 16
 class SexChoicesMixin(models.Model):
17
+    UNKNOWN = 0
17 18
     MALE = 1
18
-    FEMALE = 0
19
+    FEMALE = 2
19 20
 
20
-    SEX_TYPE = (
21
-        (MALE, u'男'),
22
-        (FEMALE, u'女'),
21
+    SEX_TUPLE = (
22
+        (UNKNOWN, '未知'),
23
+        (MALE, '男'),
24
+        (FEMALE, '女'),
23 25
     )
24 26
 
25 27
     class Meta:

+ 1 - 1
thermometer/local_settings_bak.py

@@ -14,4 +14,4 @@ EMAIL_HOST_USER = 'error.notify@exmail.com'
14 14
 EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'
15 15
 DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'
16 16
 ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
17
-EMAIL_SUBJECT_PREFIX = u'[Thermometer] '
17
+EMAIL_SUBJECT_PREFIX = '[Thermometer] '

+ 7 - 1
thermometer/settings.py

@@ -55,6 +55,8 @@ INSTALLED_APPS = [
55 55
     'django_we',
56 56
     'commands',
57 57
     'api',
58
+    'account',
59
+    'equipment',
58 60
 ]
59 61
 
60 62
 MIDDLEWARE = [
@@ -278,7 +280,7 @@ ADMINS = []
278 280
 MANAGERS = ADMINS
279 281
 # Subject-line prefix for email messages send with django.core.mail.mail_admins
280 282
 # or ...mail_managers.  Make sure to include the trailing space.
281
-EMAIL_SUBJECT_PREFIX = u'[Thermometer] '
283
+EMAIL_SUBJECT_PREFIX = '[Thermometer] '
282 284
 
283 285
 # Django-Admin Settings
284 286
 DJANGO_ADMIN_DISABLE_DELETE_SELECTED = False
@@ -310,6 +312,10 @@ DJANGO_WE_MODEL_DISPLAY_OR_NOT = True
310 312
 DJANGO_WE_COOKIE_MAX_AGE = COOKIE_MAX_AGE
311 313
 DJANGO_WE_COOKIE_SALT = COOKIE_SALT
312 314
 
315
+# 密码设置
316
+MAKE_PASSWORD_SALT = ''
317
+MAKE_PASSWORD_HASHER = 'pbkdf2_sha256'
318
+
313 319
 # 开发调试相关配置
314 320
 if DEBUG:
315 321
     try:

+ 47 - 24
utils/error/errno_utils.py

@@ -5,32 +5,55 @@ from StatusCode import BaseStatusCode, StatusCodeField
5 5
 
6 6
 class ParamStatusCode(BaseStatusCode):
7 7
     """ 4000xx 参数相关错误码 """
8
-    PARAM_NOT_FOUND = StatusCodeField(400000, 'Param Not Found', description=u'参数不存在')
8
+    PARAM_NOT_FOUND = StatusCodeField(400000, 'Param Not Found', description='参数不存在')
9 9
 
10 10
 
11 11
 class ProfileStatusCode(BaseStatusCode):
12 12
     """ 4001xx 用户相关错误码 """
13
-    PROFILE_NOT_FOUND = StatusCodeField(400101, 'Profile Not Found', description=u'用户不存在')
13
+    PROFILE_NOT_FOUND = StatusCodeField(400101, 'Profile Not Found', description='用户不存在')
14 14
 
15 15
 
16 16
 class PhoneStatusCode(BaseStatusCode):
17 17
     """ 4002xx 手机相关错误码 """
18
-    INVALID_PHONE = StatusCodeField(400200, 'Invalid Phone', description=u'非法手机号')
19
-    PHONE_NOT_FOUND = StatusCodeField(400201, 'Phone Not Found', description=u'手机号不存在')
20
-    PHONE_ALREADY_EXISTS = StatusCodeField(400202, 'Phone Already Exists', description=u'手机号已存在')
18
+    INVALID_PHONE = StatusCodeField(400200, 'Invalid Phone', description='非法手机号')
19
+    PHONE_NOT_FOUND = StatusCodeField(400201, 'Phone Not Found', description='手机号不存在')
20
+    PHONE_ALREADY_EXISTS = StatusCodeField(400202, 'Phone Already Exists', description='手机号已存在')
21
+
22
+
23
+class AdministratorStatusCode(BaseStatusCode):
24
+    """ 操作员相关错误码 4010xx """
25
+    ADMINISTRATOR_NOT_FOUND = StatusCodeField(401001, 'Administrator Not Found', description='管理员不存在')
26
+    # 密码
27
+    ADMINISTRATOR_PASSWORD_ERROR = StatusCodeField(401002, 'Administrator Password Error', description='管理员密码错误')
28
+    # 手机号
29
+    ADMINISTRATOR_PHONE_ALREADY_EXISTS = StatusCodeField(401005, 'Administrator Phone Already Exists', description='管理员手机号已经存在')
30
+    # 状态
31
+    ADMINISTRATOR_NOT_ACTIVATED = StatusCodeField(401015, 'Administrator Not Activated', description='管理员未激活')
32
+    ADMINISTRATOR_HAS_DISABLED = StatusCodeField(401016, 'Administrator Has Disabled', description='管理员已禁用')
33
+    ADMINISTRATOR_HAS_DELETED = StatusCodeField(401017, 'Administrator Has Deleted', description='管理员已删除')
34
+
35
+
36
+class IsolationPointStatusCode(BaseStatusCode):
37
+    """ 操作员相关错误码 4020xx """
38
+    ISOLATIONPOINT_NOT_FOUND = StatusCodeField(402001, 'IsolationPoint Not Found', description='隔离点不存在')
39
+
40
+
41
+class ThermometerEquipmentStatusCode(BaseStatusCode):
42
+    """ 操作员相关错误码 4030xx """
43
+    THERMOMETER_EQUIPMENT_NOT_FOUND = StatusCodeField(403001, 'Thermometer Equipment Not Found', description='测温设备不存在')
21 44
 
22 45
 
23 46
 class OrderStatusCode(BaseStatusCode):
24 47
     """ 4040xx 订单/支付相关错误码 """
25
-    UNIFIED_ORDER_FAIL = StatusCodeField(404000, 'Unified Order Fail', description=u'统一下单失败')
26
-    ORDER_NOT_FOUND = StatusCodeField(404001, 'Order Not Found', description=u'订单不存在')
48
+    UNIFIED_ORDER_FAIL = StatusCodeField(404000, 'Unified Order Fail', description='统一下单失败')
49
+    ORDER_NOT_FOUND = StatusCodeField(404001, 'Order Not Found', description='订单不存在')
27 50
     # 订单支付状态
28
-    ORDER_NOT_PAY = StatusCodeField(404011, 'Order Not Pay', description=u'订单未支付')
29
-    ORDER_PAYING = StatusCodeField(404012, 'Order Paying', description=u'订单支付中')
30
-    ORDER_PAY_FAIL = StatusCodeField(404013, 'Order Pay Fail', description=u'微信支付失败')
51
+    ORDER_NOT_PAY = StatusCodeField(404011, 'Order Not Pay', description='订单未支付')
52
+    ORDER_PAYING = StatusCodeField(404012, 'Order Paying', description='订单支付中')
53
+    ORDER_PAY_FAIL = StatusCodeField(404013, 'Order Pay Fail', description='微信支付失败')
31 54
     # 通知校验状态
32
-    SIGN_CHECK_FAIL = StatusCodeField(404090, 'Sign Check Fail', description=u'签名校验失败')
33
-    FEE_CHECK_FAIL = StatusCodeField(404091, 'FEE Check Fail', description=u'金额校验失败')
55
+    SIGN_CHECK_FAIL = StatusCodeField(404090, 'Sign Check Fail', description='签名校验失败')
56
+    FEE_CHECK_FAIL = StatusCodeField(404091, 'FEE Check Fail', description='金额校验失败')
34 57
 
35 58
 
36 59
 class PayStatusCode(BaseStatusCode):
@@ -39,39 +62,39 @@ class PayStatusCode(BaseStatusCode):
39 62
 
40 63
 class WithdrawStatusCode(BaseStatusCode):
41 64
     """ 4042xx 提现相关错误码 """
42
-    BALANCE_INSUFFICIENT = StatusCodeField(404200, 'Balance Insufficient', description=u'提现金额不足')
65
+    BALANCE_INSUFFICIENT = StatusCodeField(404200, 'Balance Insufficient', description='提现金额不足')
43 66
 
44 67
 
45 68
 class TokenStatusCode(BaseStatusCode):
46 69
     """ 4090xx 票据相关错误码 """
47
-    TOKEN_NOT_FOUND = StatusCodeField(409001, 'Token Not Found', description=u'票据不存在')
70
+    TOKEN_NOT_FOUND = StatusCodeField(409001, 'Token Not Found', description='票据不存在')
48 71
 
49 72
 
50 73
 class SignatureStatusCode(BaseStatusCode):
51 74
     """ 4091xx 签名校验错误 """
52
-    SIGNATURE_ERROR = StatusCodeField(409101, 'Signature Error', description=u'签名错误')
75
+    SIGNATURE_ERROR = StatusCodeField(409101, 'Signature Error', description='签名错误')
53 76
 
54 77
 
55 78
 class GVCodeStatusCode(BaseStatusCode):
56 79
     """ 4092xx 图形验证码相关错误码 """
57
-    GRAPHIC_VCODE_ERROR = StatusCodeField(409201, 'Graphic VCode Error', description=u'图形验证码错误')
80
+    GRAPHIC_VCODE_ERROR = StatusCodeField(409201, 'Graphic VCode Error', description='图形验证码错误')
58 81
 
59 82
 
60 83
 class SVCodeStatusCode(BaseStatusCode):
61 84
     """ 4093xx 短信验证码相关错误码 """
62
-    SMS_QUOTA_LIMIT = StatusCodeField(409300, 'SMS Quota Limit', description=u'短信次数超限')
63
-    SMS_VCODE_ERROR = StatusCodeField(409301, 'SMS VCode Error', description=u'验证码错误,请稍后重试')
64
-    SMS_VCODE_HAS_SEND = StatusCodeField(409302, 'SMS VCode Has Send', description=u'验证码已发送,请勿重复获取')
85
+    SMS_QUOTA_LIMIT = StatusCodeField(409300, 'SMS Quota Limit', description='短信次数超限')
86
+    SMS_VCODE_ERROR = StatusCodeField(409301, 'SMS VCode Error', description='验证码错误,请稍后重试')
87
+    SMS_VCODE_HAS_SEND = StatusCodeField(409302, 'SMS VCode Has Send', description='验证码已发送,请勿重复获取')
65 88
 
66 89
 
67 90
 class InsufficientStatusCode(BaseStatusCode):
68 91
     """ 4095xx 不足相关错误码 """
69
-    BALANCE_INSUFFICIENT = StatusCodeField(409501, 'Balance Insufficient', description=u'余额不足')
70
-    INTEGRAL_INSUFFICIENT = StatusCodeField(409502, 'Integral Insufficient', description=u'积分不足')
92
+    BALANCE_INSUFFICIENT = StatusCodeField(409501, 'Balance Insufficient', description='余额不足')
93
+    INTEGRAL_INSUFFICIENT = StatusCodeField(409502, 'Integral Insufficient', description='积分不足')
71 94
 
72 95
 
73 96
 class PermissionStatusCode(BaseStatusCode):
74 97
     """ 4099xx 权限相关错误码 """
75
-    PERMISSION_DENIED = StatusCodeField(409900, 'Permission Denied', description=u'权限不足')
76
-    UPLOAD_PERMISSION_DENIED = StatusCodeField(409910, 'Upload Permission Denied', description=u'上传权限不足')
77
-    UPDATE_PERMISSION_DENIED = StatusCodeField(409930, 'Update Permission Denied', description=u'更新权限不足')
98
+    PERMISSION_DENIED = StatusCodeField(409900, 'Permission Denied', description='权限不足')
99
+    UPLOAD_PERMISSION_DENIED = StatusCodeField(409910, 'Upload Permission Denied', description='上传权限不足')
100
+    UPDATE_PERMISSION_DENIED = StatusCodeField(409930, 'Update Permission Denied', description='更新权限不足')

adminSystem - Gogs: Go Git Service

Aucune description

FFIB: 11e3a9652a first 7 ans auparavant
..
node_modules 11e3a9652a first 7 ans auparavant
LICENSE 11e3a9652a first 7 ans auparavant
README.md 11e3a9652a first 7 ans auparavant
index.js 11e3a9652a first 7 ans auparavant
package.json 11e3a9652a first 7 ans auparavant

README.md

snapdragon-node NPM version NPM monthly downloads NPM total downloads Linux Build Status

Snapdragon utility for creating a new AST node in custom code, such as plugins.

Install

Install with npm:

$ npm install --save snapdragon-node

Usage

With snapdragon v0.9.0 and higher you can use this.node() to create a new Node, whenever it makes sense.

var Node = require('snapdragon-node');
var Snapdragon = require('snapdragon');
var snapdragon = new Snapdragon();

// example usage inside a parser visitor function
snapdragon.parser.set('foo', function() {
  // get the current "start" position
  var pos = this.position();

  // returns the match if regex matches the substring 
  // at the current position on `parser.input`
  var match = this.match(/foo/);
  if (match) {
    // call "pos" on the node, to set the start and end 
    // positions, and return the node to push it onto the AST
    // (snapdragon will push the node onto the correct
    // nodes array, based on the stack)
    return pos(new Node({type: 'bar', val: match[0]}));
  }
});

API

Node

Create a new AST Node with the given val and type.

Params

  • val {String|Object}: Pass a matched substring, or an object to merge onto the node.
  • type {String}: The node type to use when val is a string.
  • returns {Object}: node instance

Example

var node = new Node('*', 'Star');
var node = new Node({type: 'star', val: '*'});

.isNode

Returns true if the given value is a node.

Params

  • node {Object}
  • returns {Boolean}

Example

var Node = require('snapdragon-node');
var node = new Node({type: 'foo'});
console.log(Node.isNode(node)); //=> true
console.log(Node.isNode({})); //=> false

.define

Define a non-enumberable property on the node instance. Useful for adding properties that shouldn't be extended or visible during debugging.

Params

  • name {String}
  • val {any}
  • returns {Object}: returns the node instance

Example

var node = new Node();
node.define('foo', 'something non-enumerable');

.isEmpty

Returns true if node.val is an empty string, or node.nodes does not contain any non-empty text nodes.

Params

  • fn {Function}: (optional) Filter function that is called on node and/or child nodes. isEmpty will return false immediately when the filter function returns false on any nodes.
  • returns {Boolean}

Example

var node = new Node({type: 'text'});
node.isEmpty(); //=> true
node.val = 'foo';
node.isEmpty(); //=> false

.push

Given node foo and node bar, push node bar onto foo.nodes, and set foo as bar.parent.

Params

  • node {Object}
  • returns {Number}: Returns the length of node.nodes

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
foo.push(bar);

.unshift

Given node foo and node bar, unshift node bar onto foo.nodes, and set foo as bar.parent.

Params

  • node {Object}
  • returns {Number}: Returns the length of node.nodes

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
foo.unshift(bar);

.pop

Pop a node from node.nodes.

  • returns {Number}: Returns the popped node

Example

var node = new Node({type: 'foo'});
node.push(new Node({type: 'a'}));
node.push(new Node({type: 'b'}));
node.push(new Node({type: 'c'}));
node.push(new Node({type: 'd'}));
console.log(node.nodes.length);
//=> 4
node.pop();
console.log(node.nodes.length);
//=> 3

.shift

Shift a node from node.nodes.

  • returns {Object}: Returns the shifted node

Example

var node = new Node({type: 'foo'});
node.push(new Node({type: 'a'}));
node.push(new Node({type: 'b'}));
node.push(new Node({type: 'c'}));
node.push(new Node({type: 'd'}));
console.log(node.nodes.length);
//=> 4
node.shift();
console.log(node.nodes.length);
//=> 3

.remove

Remove node from node.nodes.

Params

  • node {Object}
  • returns {Object}: Returns the removed node.

Example

node.remove(childNode);

.find

Get the first child node from node.nodes that matches the given type. If type is a number, the child node at that index is returned.

Params

  • type {String}
  • returns {Object}: Returns a child node or undefined.

Example

var child = node.find(1); //<= index of the node to get
var child = node.find('foo'); //<= node.type of a child node
var child = node.find(/^(foo|bar)$/); //<= regex to match node.type
var child = node.find(['foo', 'bar']); //<= array of node.type(s)

.isType

Return true if the node is the given type.

Params

  • type {String}
  • returns {Boolean}

Example

var node = new Node({type: 'bar'});
cosole.log(node.isType('foo'));          // false
cosole.log(node.isType(/^(foo|bar)$/));  // true
cosole.log(node.isType(['foo', 'bar'])); // true

.hasType

Return true if the node.nodes has the given type.

Params

  • type {String}
  • returns {Boolean}

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
foo.push(bar);

cosole.log(foo.hasType('qux'));          // false
cosole.log(foo.hasType(/^(qux|bar)$/));  // true
cosole.log(foo.hasType(['qux', 'bar'])); // true
  • returns {Array}

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
var baz = new Node({type: 'baz'});
foo.push(bar);
foo.push(baz);

console.log(bar.siblings.length) // 2
console.log(baz.siblings.length) // 2
  • returns {Number}

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
var baz = new Node({type: 'baz'});
var qux = new Node({type: 'qux'});
foo.push(bar);
foo.push(baz);
foo.unshift(qux);

console.log(bar.index) // 1
console.log(baz.index) // 2
console.log(qux.index) // 0
  • returns {Object}

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
var baz = new Node({type: 'baz'});
foo.push(bar);
foo.push(baz);

console.log(baz.prev.type) // 'bar'
  • returns {Object}

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
var baz = new Node({type: 'baz'});
foo.push(bar);
foo.push(baz);

console.log(bar.siblings.length) // 2
console.log(baz.siblings.length) // 2
  • returns {Object}: The first node, or undefiend

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
var baz = new Node({type: 'baz'});
var qux = new Node({type: 'qux'});
foo.push(bar);
foo.push(baz);
foo.push(qux);

console.log(foo.first.type) // 'bar'
  • returns {Object}: The last node, or undefiend

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
var baz = new Node({type: 'baz'});
var qux = new Node({type: 'qux'});
foo.push(bar);
foo.push(baz);
foo.push(qux);

console.log(foo.last.type) // 'qux'
  • returns {Object}: The last node, or undefiend

Example

var foo = new Node({type: 'foo'});
var bar = new Node({type: 'bar'});
var baz = new Node({type: 'baz'});
var qux = new Node({type: 'qux'});
foo.push(bar);
foo.push(baz);
foo.push(qux);

console.log(foo.last.type) // 'qux'

Release history

Changelog entries are classified using the following labels from keep-a-changelog:

  • added: for new features
  • changed: for changes in existing functionality
  • deprecated: for once-stable features removed in upcoming releases
  • removed: for deprecated features removed in this release
  • fixed: for any bug fixes

Custom labels used in this changelog:

  • dependencies: bumps dependencies
  • housekeeping: code re-organization, minor edits, or other changes that don't fit in one of the other categories.

[2.0.0] - 2017-05-01

Changed

  • .unshiftNode was renamed to .unshift
  • .pushNode was renamed to .push
  • .getNode was renamed to .find

Added

[0.1.0]

First release.

About

Related projects

Contributing

Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.

Please read the contributing guide for advice on opening issues, pull requests, and coding standards.

Building docs

(This project's readme.md is generated by verb, please don't edit the readme directly. Any changes to the readme must be made in the .verb.md readme template.)

To generate the readme, run the following command:

$ npm install -g verbose/verb#dev verb-generate-readme && verb

Running tests

Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:

$ npm install && npm test

Author

Jon Schlinkert

License

Copyright © 2017, Jon Schlinkert. Released under the MIT License.


This file was generated by verb-generate-readme, v0.6.0, on June 25, 2017.