django-transcribe 0.5.16__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {django_transcribe-0.5.16.dist-info → django_transcribe-0.6.0.dist-info}/METADATA +3 -8
  2. {django_transcribe-0.5.16.dist-info → django_transcribe-0.6.0.dist-info}/RECORD +30 -26
  3. {django_transcribe-0.5.16.dist-info → django_transcribe-0.6.0.dist-info}/WHEEL +1 -1
  4. transcribe/__init__.py +1 -1
  5. transcribe/admin.py +56 -11
  6. transcribe/apps.py +1 -2
  7. transcribe/filters.py +1 -2
  8. transcribe/forms.py +2 -2
  9. transcribe/migrations/0018_point_fks_to_user.py +40 -0
  10. transcribe/migrations/0019_create_transcribeuser.py +43 -0
  11. transcribe/migrations/0020_populate_transcribe_users.py +18 -0
  12. transcribe/migrations/0021_point_fks_to_transcribe_user.py +57 -0
  13. transcribe/models.py +38 -22
  14. transcribe/signals.py +10 -1
  15. transcribe/templates/transcribe/reports/list.html +1 -1
  16. transcribe/templates/transcribe/reports/projects.html +0 -19
  17. transcribe/templates/transcribe/reports/users.html +8 -8
  18. transcribe/templates/transcribe/web/dashboard.html +0 -5
  19. transcribe/templates/transcribe/web/project.html +1 -3
  20. transcribe/templates/transcribe/web/project_edit.html +0 -3
  21. transcribe/templates/transcribe/web/task_edit.html +22 -21
  22. transcribe/templates/transcribe/web/user.html +2 -57
  23. transcribe/templates/transcribe/web/users_list.html +11 -37
  24. transcribe/urls.py +27 -33
  25. transcribe/views/mixins.py +1 -1
  26. transcribe/views/reports.py +40 -28
  27. transcribe/views/web.py +15 -4
  28. {django_transcribe-0.5.16.dist-info → django_transcribe-0.6.0.dist-info}/AUTHORS +0 -0
  29. {django_transcribe-0.5.16.dist-info → django_transcribe-0.6.0.dist-info}/LICENSE +0 -0
  30. {django_transcribe-0.5.16.dist-info → django_transcribe-0.6.0.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-transcribe
3
- Version: 0.5.16
3
+ Version: 0.6.0
4
4
  Summary: crowd source transcription
5
5
  Home-page: https://gitlab.com/byuhbll/lib/python/django-transcribe
6
6
  Author: BYU HBLL WebDev team
7
7
  Author-email: support@lib.byu.edu
8
- License: UNKNOWN
9
- Platform: UNKNOWN
10
8
  Classifier: Intended Audience :: Developers
11
9
  Classifier: License :: OSI Approved :: BSD License
12
10
  Classifier: Operating System :: OS Independent
@@ -14,11 +12,8 @@ Classifier: Programming Language :: Python
14
12
  Classifier: Programming Language :: Python :: 3.6
15
13
  License-File: LICENSE
16
14
  License-File: AUTHORS
17
- Requires-Dist: requests
18
15
  Requires-Dist: django-model-utils
19
- Requires-Dist: django-spurl
20
- Requires-Dist: lxml (<=3.5.0)
21
- Requires-Dist: Django (<2.3.0,>=2.2.0)
16
+ Requires-Dist: lxml
17
+ Requires-Dist: Django <2.3.0,>=2.2.0
22
18
 
23
19
  Crowd source the transcription of texts, audio, and video.
24
-
@@ -1,15 +1,15 @@
1
- transcribe/__init__.py,sha256=5PMkkgpwYDd_NN2UsyIXQurHxAStUwmSTvf9xzEfgh8,79
2
- transcribe/admin.py,sha256=pTo2UGPoekpsq3fNY0ve4MeUlmghO5PQXhfuNuXrZJY,5575
1
+ transcribe/__init__.py,sha256=qFp_T9xwXsjX5GhZe6N2ShySfSHh7oBCM8OlR_KtO4Y,78
2
+ transcribe/admin.py,sha256=HwrJafMjSoIKgb_En0zNvZ3nk5rCZWtJ5n0DdGU9nfI,6512
3
3
  transcribe/api.py,sha256=2m_y44ilQh87CWksHHqiE4A14javR8omlXEXm65lmC4,74
4
- transcribe/apps.py,sha256=M07nb8B159r2Lo8v3Q1TjcDLlbM6GvHjXmHjzK7y0Fs,256
4
+ transcribe/apps.py,sha256=XUdPQkCjpqWAKDCIaE6Mh8Q4xN5upKv95MG1u2_5pig,246
5
5
  transcribe/diff_match_patch.py,sha256=HZC5DNab5OVwbB4Zc7G17l1OHbBlH2g9-xOtT1zkQ1A,84937
6
- transcribe/filters.py,sha256=9po0mrCKbEpkhB2NZJ8Ol9roEebI1rdVGeXyjmvAO_E,1321
7
- transcribe/forms.py,sha256=VJueMilHUTVH3aLIKt6x81Ogf1kPiWMPotdDAEmm_OI,5102
6
+ transcribe/filters.py,sha256=_ANh6XGDXgmD_wxQo5IPReQsv3_MDqThOwB06LKfbac,1264
7
+ transcribe/forms.py,sha256=iEeZsgWGlYGw0mw81puzhxv91nDefmrjwQ_TdSr7Zfc,5117
8
8
  transcribe/middleware.py,sha256=3SvvNmJMlVohD_4Liv1yT3hc_nudpvt2giAE7Wt9rx4,485
9
- transcribe/models.py,sha256=pCLDtWTmrI6HhA3HRtGVZg4E79NHKTJy-cqSRxKhOm4,38598
9
+ transcribe/models.py,sha256=g0_rtkLKbrkwxbWQbfZ3q5rnug235aEzFCI5DAUrcI4,38965
10
10
  transcribe/settings.py,sha256=TNRFJstZF9X3l5qZSzv3xcrEuPMmOQbB-59mqADn9Dk,569
11
- transcribe/signals.py,sha256=YMxsVgh07TjH29SLWWjoMpou-q7rEA65ZyP9aavtQL4,869
12
- transcribe/urls.py,sha256=yI2P7q4QBrfsl5Zn4C152rPMjSNMtxLDmkx8c2d6VuA,2414
11
+ transcribe/signals.py,sha256=pvRSWobg__Jw5ZvlFBZZAw3rDk1dvVyiq8Ka2lBGeSY,1105
12
+ transcribe/urls.py,sha256=xgTkOdeC1cWmQAMzAp-VRwvmwyEAfEabncGgHyZiAL0,2040
13
13
  transcribe/utils.py,sha256=k8OFWqO5vU3jUCJsLZmJiTK0Rlrb0Yf-rqqfDJc2HZw,3747
14
14
  transcribe/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  transcribe/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -33,6 +33,10 @@ transcribe/migrations/0014_auto_20160203_1544.py,sha256=jc0GDUKuj2m4T9FIX6Q-guz0
33
33
  transcribe/migrations/0015_auto_20160309_1342.py,sha256=vy2_6J3ax_znwR5DjVhhsbw3GoS3sl3VEa9gA4j6Gks,596
34
34
  transcribe/migrations/0016_auto_20160817_1343.py,sha256=wQjpwUXABGLZ1X_noITkBQ1BCaGQSxwRa-cN8wySpjg,391
35
35
  transcribe/migrations/0017_auto_20191115_1123.py,sha256=hixGTGYWGKOQakeiL4ISq_97FFPgg9chPEykphDHDt0,866
36
+ transcribe/migrations/0018_point_fks_to_user.py,sha256=qqni_HVTvp1ENajKAuMf2fyps8BNERrwP28Hcmd7TOw,1214
37
+ transcribe/migrations/0019_create_transcribeuser.py,sha256=mLd8rOFfSkI_5t27AFhckpu-RmntN-T9k4Osm-K7skQ,1311
38
+ transcribe/migrations/0020_populate_transcribe_users.py,sha256=nkB5v7vCNLBibR-sENivKBmJY4Yf-VIImZqy4AQs6oA,504
39
+ transcribe/migrations/0021_point_fks_to_transcribe_user.py,sha256=V4LAQ7xkVGc0oAntppKV9CiQj0YBVzSOKco5I-cN_eM,1781
36
40
  transcribe/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
41
  transcribe/static/transcribe/favicon.ico,sha256=4towU7vPOvBDh1bq1MT-ZPI_I16Ia0mWcbgtH8rBHCk,318
38
42
  transcribe/static/transcribe/css/icons.css,sha256=1Nd20ZYLtTCK-UvBMqQBxfWq6YmHNeW2Wn-HHcKaDQI,24346
@@ -119,31 +123,31 @@ transcribe/templates/registration/login.html,sha256=Hh0FFE270p5eyTFbZLWB_K1KfBKy
119
123
  transcribe/templates/transcribe/.DS_Store,sha256=1lFlJ5EFymdzGAUAaI30vcaaLHt3F1LwpG7xILf9jsM,6148
120
124
  transcribe/templates/transcribe/errors/404.html,sha256=sWTl4eyIg0ysMiOo6wLg9egGGDLJ9BELoOhLXEEnGrg,178
121
125
  transcribe/templates/transcribe/errors/500.html,sha256=B0W2NZiGcQrnFgQIXPefF02ZVAwrXEAm8Ohjnnps-hQ,195
122
- transcribe/templates/transcribe/reports/list.html,sha256=7YVx3Y_Hyl4TuS63Ycb6Yhkxc1mvuMoLU7pJdtQJ3WU,3851
123
- transcribe/templates/transcribe/reports/projects.html,sha256=Jce3ET2WWgykjKV99Ysh8hftBREcLRa1r8p7Bl8XUDU,2664
124
- transcribe/templates/transcribe/reports/users.html,sha256=43IdBTm6NvQ-_Rpam4K_I149ZjBIxPzVV8IVuxrkR_o,1280
126
+ transcribe/templates/transcribe/reports/list.html,sha256=ovEtVFknwhz89wS1nQjeQ1xwk3acmmtCEgZFKw_KuqI,3848
127
+ transcribe/templates/transcribe/reports/projects.html,sha256=wHkcS_DdtU5xoiOHP9RAnieUwDzbttsIKlYxm_MfGo4,1639
128
+ transcribe/templates/transcribe/reports/users.html,sha256=51j4pSrmIM4G0caqeQFXkeIIzgeggnqOlr039jus2ac,1427
125
129
  transcribe/templates/transcribe/web/base.html,sha256=zAP49q4DX6bqSn4dBi9IQ8RTy2AsYI8YDDTo4HfDtsg,51
126
130
  transcribe/templates/transcribe/web/base_transcribe.html,sha256=KU3J2SRMPu4D14YgdT7Eaiv7BOI0s2e1o195SPf69vI,4048
127
- transcribe/templates/transcribe/web/dashboard.html,sha256=m76bE9Q36bNz-JVhywa4mnTsy6BvYajgtNPg96nha6E,4213
131
+ transcribe/templates/transcribe/web/dashboard.html,sha256=7CTABKnepEIlL4hygi-WBzkqbfgRiXm60XQ8ES_wAUA,4061
128
132
  transcribe/templates/transcribe/web/faq.html,sha256=5ZqGWLsLh3Q3DBmZQXUvZtPJvkIOvUUwL5HLdPyjet8,10192
129
133
  transcribe/templates/transcribe/web/help.html,sha256=NjbUm4ZEgu60mFsZdB1nRsmSE7ynzlQ4QgGskVT9DZ8,2482
130
134
  transcribe/templates/transcribe/web/index.html,sha256=faHWxkKAxRZNZcVIi1nG8rLBkQqnaSfXudVNhbMMTGI,133
131
135
  transcribe/templates/transcribe/web/landing_page.html,sha256=6jEHXMYNXhKsiHJ2NhSm_TTBVQEKUZPma9X6LwxSLPo,376
132
- transcribe/templates/transcribe/web/project.html,sha256=vtEMBbddP6GkCccQnYVl5Hb6uXHglCJy0mn_rI9z85w,3335
136
+ transcribe/templates/transcribe/web/project.html,sha256=UGxtvknONkiUT3iFuloKPwohnYPohokHhk1FkTE_3xg,3136
133
137
  transcribe/templates/transcribe/web/project_confirm_delete.html,sha256=KXZm8nh6J5yErbYY0l2JfhIpQVyS9QaoXp1mGgbQtpE,426
134
- transcribe/templates/transcribe/web/project_edit.html,sha256=B2uKgNatbFVFTXsuHC44upyv-R9zXylnB_L65eyAhKo,944
138
+ transcribe/templates/transcribe/web/project_edit.html,sha256=UJaHyRCUTf9qbHYTG1MKDzAlLYz7L7UBhJw-Ixtt0Q8,568
135
139
  transcribe/templates/transcribe/web/projects_list.html,sha256=-Ib74x7gLND4Q18wUo-JXmmmnpo8nbP-gVJ0lObfvWg,1300
136
- transcribe/templates/transcribe/web/task_edit.html,sha256=VDh1Q6vujB_OfAGlITd_3R-l8WRAtHuCouaOdN3LWTY,9403
137
- transcribe/templates/transcribe/web/user.html,sha256=2osZ4SyqJgnbS-c2qF1rOTdngl7CuAat0n6XXcAVqNY,3289
138
- transcribe/templates/transcribe/web/users_list.html,sha256=gUgsGlJfJWiRTIhgnVXa49ONEc8K1J7PnLLEmCJPjIk,2921
140
+ transcribe/templates/transcribe/web/task_edit.html,sha256=JWisOHN-bd2gh3t0RB8BiuS8Wc3IwRzt6cA_9DiB0Tw,9219
141
+ transcribe/templates/transcribe/web/user.html,sha256=-EzYdRpc383iL31lmNRv4gE-mwFO8L1KeMNGlt633rM,863
142
+ transcribe/templates/transcribe/web/users_list.html,sha256=uXbaDTXXXOJ9iWuw6OoKRXM6B2--qhswGOv6DvYggRU,1830
139
143
  transcribe/templates/transcribe/xsl/tei-html.xsl,sha256=oOVnAZhDVDVzrQR5UEbNV-97ZBTfNfzKDThiUr9EGCc,2984
140
144
  transcribe/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
- transcribe/views/mixins.py,sha256=46WewH6ZTFnRhsNM_zJPsrfOqTzVSmN6mZ3mkH0HmOo,2728
142
- transcribe/views/reports.py,sha256=OilTUjJn2RsxBUSFxQu-1gLQD_jftEu_1OhiHwQ2y10,12876
143
- transcribe/views/web.py,sha256=5lBhd70cDPjf0zCxiSAoLPdFIOihEUu63AzNnZMkEPU,14830
144
- django_transcribe-0.5.16.dist-info/AUTHORS,sha256=o63LB-1ge8sViGo8pRhfL8KTfgnwoSqAhTR7_iEkJXo,527
145
- django_transcribe-0.5.16.dist-info/LICENSE,sha256=I9xp13yeayqZikrv7rQrzsDWviTwy1R0phFlKtM1meg,1472
146
- django_transcribe-0.5.16.dist-info/METADATA,sha256=GijXjfNRg_GKPYzz43_YA0w_un-60ZwaDuyj1Gw9FDM,758
147
- django_transcribe-0.5.16.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
148
- django_transcribe-0.5.16.dist-info/top_level.txt,sha256=NljSay7zO6czcjqjVmivw6VW58aXlYHVZQvnwmpy08g,11
149
- django_transcribe-0.5.16.dist-info/RECORD,,
145
+ transcribe/views/mixins.py,sha256=QmFDhWb5VCEaGatLdbsTK72jiBG2OKrlncnz79FUmto,2727
146
+ transcribe/views/reports.py,sha256=7YD5E6CPsIfWP8hG8IFTek29cM2T29NHuowAdlTAyBs,13408
147
+ transcribe/views/web.py,sha256=sU5FNh0jl1yKSRZFo5CvdD5CVV9sb9vtUNdLRZuzAQg,15213
148
+ django_transcribe-0.6.0.dist-info/AUTHORS,sha256=o63LB-1ge8sViGo8pRhfL8KTfgnwoSqAhTR7_iEkJXo,527
149
+ django_transcribe-0.6.0.dist-info/LICENSE,sha256=I9xp13yeayqZikrv7rQrzsDWviTwy1R0phFlKtM1meg,1472
150
+ django_transcribe-0.6.0.dist-info/METADATA,sha256=a9Q9OZ44OUSDojSBX3-cx-jFVZ_1l7uGh1MGYQ5F7-I,657
151
+ django_transcribe-0.6.0.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
152
+ django_transcribe-0.6.0.dist-info/top_level.txt,sha256=NljSay7zO6czcjqjVmivw6VW58aXlYHVZQvnwmpy08g,11
153
+ django_transcribe-0.6.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.0)
2
+ Generator: setuptools (74.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
transcribe/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  default_app_config = 'transcribe.apps.transcribeConfig'
2
- __version__ = '0.5.16'
2
+ __version__ = '0.6.0'
transcribe/admin.py CHANGED
@@ -25,9 +25,9 @@ class UserTaskAdmin(admin.ModelAdmin):
25
25
  'task__file',
26
26
  'status',
27
27
  'task_type',
28
- 'user__username',
29
- 'user__first_name',
30
- 'user__last_name',
28
+ 'user__user__username',
29
+ 'user__user__first_name',
30
+ 'user__user__last_name',
31
31
  'task__project__title',
32
32
  )
33
33
  fields = (
@@ -168,17 +168,32 @@ class TagAdmin(admin.ModelAdmin):
168
168
 
169
169
  @admin.register(models.TranscribeUser)
170
170
  class TranscribeUserAdmin(admin.ModelAdmin):
171
- readonly_fields = ['last_login', 'usertasks_link']
172
- fields = [
171
+ readonly_fields = (
173
172
  'username',
174
173
  'first_name',
175
174
  'last_name',
176
175
  'email',
177
- 'is_active',
178
- 'last_login',
179
176
  'groups',
180
177
  'usertasks_link',
181
- ]
178
+ )
179
+
180
+ fieldsets = (
181
+ (
182
+ None,
183
+ {
184
+ 'fields': (
185
+ 'user',
186
+ 'username',
187
+ 'first_name',
188
+ 'last_name',
189
+ 'email',
190
+ 'groups',
191
+ 'usertasks_link',
192
+ )
193
+ },
194
+ ),
195
+ )
196
+
182
197
  list_display = [
183
198
  'username',
184
199
  'first_name',
@@ -191,10 +206,37 @@ class TranscribeUserAdmin(admin.ModelAdmin):
191
206
  'num_skipped_reviews',
192
207
  'num_in_progress_reviews',
193
208
  ]
194
- search_fields = ['username', 'first_name', 'last_name', 'email']
209
+ search_fields = [
210
+ 'user__username',
211
+ 'user__first_name',
212
+ 'user__last_name',
213
+ 'user__email',
214
+ ]
195
215
 
196
- def get_queryset(self, request):
197
- return self.model.objects.filter(is_active=True)
216
+ def username(self, obj):
217
+ return obj.user.username
218
+
219
+ username.short_description = 'Username'
220
+
221
+ def first_name(self, obj):
222
+ return obj.user.first_name
223
+
224
+ first_name.short_description = 'First name'
225
+
226
+ def last_name(self, obj):
227
+ return obj.user.last_name
228
+
229
+ last_name.short_description = 'Last name'
230
+
231
+ def email(self, obj):
232
+ return obj.user.email
233
+
234
+ email.short_description = 'Email'
235
+
236
+ def groups(self, obj):
237
+ return ', '.join([group.name for group in obj.user.groups.all()])
238
+
239
+ groups.short_description = 'Groups'
198
240
 
199
241
  def usertasks_link(self, obj):
200
242
  url = resolve_url('admin:transcribe_usertask_changelist')
@@ -205,3 +247,6 @@ class TranscribeUserAdmin(admin.ModelAdmin):
205
247
  )
206
248
 
207
249
  usertasks_link.short_description = 'User tasks'
250
+
251
+ def get_queryset(self, request):
252
+ return self.model.objects.filter(user__is_active=True)
transcribe/apps.py CHANGED
@@ -10,5 +10,4 @@ class transcribeConfig(AppConfig):
10
10
  verbose_name = 'transcribe'
11
11
 
12
12
  def ready(self):
13
- pass
14
- # from . import signals # noqa
13
+ from . import signals # noqa F401
transcribe/filters.py CHANGED
@@ -12,14 +12,13 @@ class UserFilterForUserTask(admin.SimpleListFilter):
12
12
  user_id = request.GET.get('user_id')
13
13
  if not user_id:
14
14
  return []
15
- user = models.TranscribeUser.objects.get(pk=user_id)
15
+ user = models.TranscribeUser.objects.get(user__pk=user_id)
16
16
  return [(user_id, user.name.strip() or user)]
17
17
 
18
18
  def queryset(self, request, queryset):
19
19
  user_pk = request.GET.get('user_id')
20
20
  if not user_pk:
21
21
  return queryset
22
- # user = models.TranscribeUser.objects.get(pk=user_pk)
23
22
  return queryset.filter(user=user_pk)
24
23
 
25
24
 
transcribe/forms.py CHANGED
@@ -1,4 +1,4 @@
1
- import cgi
1
+ import html
2
2
  import logging
3
3
 
4
4
  from django import forms
@@ -96,7 +96,7 @@ class ProjectForm(NoColon):
96
96
 
97
97
  def get_processed_text(self, text):
98
98
  """Given unprocessed text, process it and return the result."""
99
- text = cgi.escape(text) # HTML encode
99
+ text = html.escape(text, quote=False) # HTML encode
100
100
  text = text.replace('ÿþ', '').replace('þÿ', '') # remove UTF-16 BOM
101
101
  return text
102
102
 
@@ -0,0 +1,40 @@
1
+ # Generated by Django 2.2.6 on 2024-08-19 23:49
2
+
3
+ import django.db.models.deletion
4
+ from django.conf import settings
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [('transcribe', '0017_auto_20191115_1123')]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='userpreferences',
15
+ name='user',
16
+ field=models.OneToOneField(
17
+ on_delete=django.db.models.deletion.CASCADE,
18
+ related_name='preferences',
19
+ to=settings.AUTH_USER_MODEL,
20
+ ),
21
+ ),
22
+ migrations.AlterField(
23
+ model_name='userprojectpreferences',
24
+ name='user',
25
+ field=models.ForeignKey(
26
+ on_delete=django.db.models.deletion.CASCADE,
27
+ related_name='projects',
28
+ to=settings.AUTH_USER_MODEL,
29
+ ),
30
+ ),
31
+ migrations.AlterField(
32
+ model_name='usertask',
33
+ name='user',
34
+ field=models.ForeignKey(
35
+ on_delete=django.db.models.deletion.CASCADE,
36
+ related_name='tasks',
37
+ to=settings.AUTH_USER_MODEL,
38
+ ),
39
+ ),
40
+ ]
@@ -0,0 +1,43 @@
1
+ # Generated by Django 2.2.6 on 2024-08-28 18:21
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
11
+ ('transcribe', '0018_point_fks_to_user'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.DeleteModel(name='TranscribeUser'),
16
+ migrations.CreateModel(
17
+ name='TranscribeUser',
18
+ fields=[
19
+ (
20
+ 'id',
21
+ models.AutoField(
22
+ auto_created=True,
23
+ primary_key=True,
24
+ serialize=False,
25
+ verbose_name='ID',
26
+ ),
27
+ ),
28
+ (
29
+ 'user',
30
+ models.OneToOneField(
31
+ on_delete=models.deletion.CASCADE,
32
+ related_name='transcribe_user',
33
+ to=settings.AUTH_USER_MODEL,
34
+ ),
35
+ ),
36
+ ],
37
+ options={
38
+ 'verbose_name': 'Transcribe user',
39
+ 'verbose_name_plural': 'Transcribe users',
40
+ 'ordering': ['user__last_name', 'user__first_name'],
41
+ },
42
+ ),
43
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 2.2.6 on 2024-08-20 15:59
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ def create_transcribe_users(apps, schema_editor):
7
+ User = apps.get_model('auth', 'User')
8
+ TranscribeUser = apps.get_model('transcribe', 'TranscribeUser')
9
+
10
+ for user in User.objects.all():
11
+ TranscribeUser.objects.create(user=user)
12
+
13
+
14
+ class Migration(migrations.Migration):
15
+
16
+ dependencies = [('transcribe', '0019_create_transcribeuser')]
17
+
18
+ operations = [migrations.RunPython(create_transcribe_users)]
@@ -0,0 +1,57 @@
1
+ # Generated by Django 2.2.6 on 2024-08-20 22:20
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [('transcribe', '0020_populate_transcribe_users')]
10
+
11
+ operations = [
12
+ migrations.AlterField(
13
+ model_name='project',
14
+ name='reviewers',
15
+ field=models.ManyToManyField(
16
+ blank=True,
17
+ related_name='review_projects',
18
+ to='transcribe.TranscribeUser',
19
+ ),
20
+ ),
21
+ migrations.AlterField(
22
+ model_name='project',
23
+ name='transcribers',
24
+ field=models.ManyToManyField(
25
+ blank=True,
26
+ related_name='transcription_projects',
27
+ to='transcribe.TranscribeUser',
28
+ ),
29
+ ),
30
+ migrations.AlterField(
31
+ model_name='userpreferences',
32
+ name='user',
33
+ field=models.OneToOneField(
34
+ on_delete=django.db.models.deletion.CASCADE,
35
+ related_name='preferences',
36
+ to='transcribe.TranscribeUser',
37
+ ),
38
+ ),
39
+ migrations.AlterField(
40
+ model_name='userprojectpreferences',
41
+ name='user',
42
+ field=models.ForeignKey(
43
+ on_delete=django.db.models.deletion.CASCADE,
44
+ related_name='projects',
45
+ to='transcribe.TranscribeUser',
46
+ ),
47
+ ),
48
+ migrations.AlterField(
49
+ model_name='usertask',
50
+ name='user',
51
+ field=models.ForeignKey(
52
+ on_delete=django.db.models.deletion.CASCADE,
53
+ related_name='tasks',
54
+ to='transcribe.TranscribeUser',
55
+ ),
56
+ ),
57
+ ]
transcribe/models.py CHANGED
@@ -38,7 +38,7 @@ TASK = Choices(TRANSCRIPTION, REVIEW)
38
38
 
39
39
  def get_transcribe_user(user):
40
40
  if not isinstance(user, TranscribeUser):
41
- user = TranscribeUser.objects.get(pk=user.pk)
41
+ user = TranscribeUser.objects.get(user=user)
42
42
  return user
43
43
 
44
44
 
@@ -64,8 +64,19 @@ def make_task_count_property(task_type, status):
64
64
  return task_counter
65
65
 
66
66
 
67
- class TranscribeUser(User):
68
- """Proxy user model to extend the default User model."""
67
+ class TranscribeUserManager(models.Manager):
68
+ """Custom manager for TranscribeUser."""
69
+
70
+ def get_queryset(self):
71
+ return super().get_queryset().select_related('user')
72
+
73
+
74
+ class TranscribeUser(models.Model):
75
+ user = models.OneToOneField(
76
+ User, related_name='transcribe_user', on_delete=models.CASCADE
77
+ )
78
+
79
+ objects = TranscribeUserManager()
69
80
 
70
81
  num_finished_transcriptions = make_task_count_property(
71
82
  TRANSCRIPTION, FINISHED
@@ -83,12 +94,12 @@ class TranscribeUser(User):
83
94
  @cached_property
84
95
  def name(self):
85
96
  """Returns the user's name."""
86
- return '{u.first_name} {u.last_name}'.format(u=self)
97
+ return '{u.first_name} {u.last_name}'.format(u=self.user)
87
98
 
88
99
  @cached_property
89
100
  def sort_name(self):
90
101
  """Returns the user's sortable name."""
91
- return '{u.last_name}, {u.first_name}'.format(u=self)
102
+ return '{u.last_name}, {u.first_name}'.format(u=self.user)
92
103
 
93
104
  @cached_property
94
105
  def is_new(self):
@@ -99,12 +110,15 @@ class TranscribeUser(User):
99
110
  @cached_property
100
111
  def is_admin(self):
101
112
  """Returns if the user is part of the Admin group or a superuser."""
102
- return self.groups.filter(name='Admin').exists() or self.is_superuser
113
+ return (
114
+ self.user.groups.filter(name='Admin').exists()
115
+ or self.user.is_superuser
116
+ )
103
117
 
104
118
  @cached_property
105
119
  def is_reviewer(self):
106
120
  """Returns if the user is part of the Reviewer group."""
107
- return self.groups.filter(name='Reviewer').exists()
121
+ return self.user.groups.filter(name='Reviewer').exists()
108
122
 
109
123
  @cached_property
110
124
  def num_tasks(self):
@@ -146,7 +160,7 @@ class TranscribeUser(User):
146
160
  return True
147
161
  # is this project is in this user's transcription_projects
148
162
  for p in self.projects_right_to_transcribe:
149
- if project.pk in p:
163
+ if project.pk == p:
150
164
  return True
151
165
  return False
152
166
 
@@ -194,7 +208,7 @@ class TranscribeUser(User):
194
208
  )
195
209
 
196
210
  def _get_report_stats(self, datetime_start, datetime_end):
197
- counts = UserTask.objects.filter(user_id=self.pk).aggregate(
211
+ counts = UserTask.objects.filter(user=self).aggregate(
198
212
  num_finished_transcriptions=Count(
199
213
  'id',
200
214
  filter=Q(
@@ -264,12 +278,13 @@ class TranscribeUser(User):
264
278
  def roles(self):
265
279
  pass
266
280
 
281
+ def __str__(self):
282
+ return self.user.username
283
+
267
284
  class Meta:
268
- proxy = True
269
- app_label = 'transcribe'
270
285
  verbose_name = 'Transcribe user'
271
286
  verbose_name_plural = 'Transcribe users'
272
- ordering = ['last_name', 'first_name']
287
+ ordering = ['user__last_name', 'user__first_name']
273
288
 
274
289
 
275
290
  class UserPreferences(models.Model):
@@ -373,10 +388,10 @@ class Project(TimeStampedModel):
373
388
  archived = models.BooleanField(default=False)
374
389
  finding_aid_url = models.CharField(blank=True, max_length=2083)
375
390
  transcribers = models.ManyToManyField(
376
- User, blank=True, related_name='transcription_projects'
391
+ TranscribeUser, blank=True, related_name='transcription_projects'
377
392
  )
378
393
  reviewers = models.ManyToManyField(
379
- User, blank=True, related_name='review_projects'
394
+ TranscribeUser, blank=True, related_name='review_projects'
380
395
  )
381
396
  allow_global_transcriptions = models.BooleanField(default=False)
382
397
 
@@ -420,7 +435,7 @@ class Project(TimeStampedModel):
420
435
  task__project=self,
421
436
  status='in progress',
422
437
  task_type=task_type,
423
- user__id=user.id,
438
+ user=user,
424
439
  ).first()
425
440
 
426
441
  def pending_transcription_task(self, user):
@@ -891,7 +906,7 @@ class Task(TimeStampedModel):
891
906
  # an instance of the TranscribeUser proxy model, and it needs to be.
892
907
  # The ids should be the same though.
893
908
  users_usertasks_for_task = (
894
- self.usertasks.filter(user__id=user.id)
909
+ self.usertasks.filter(user=user)
895
910
  .filter(task_type=TRANSCRIPTION)
896
911
  .exclude(status=FINISHED)
897
912
  )
@@ -899,7 +914,7 @@ class Task(TimeStampedModel):
899
914
  # are transcription tasks and are not finished.
900
915
  if users_usertasks_for_task.count() < 1:
901
916
  ut = UserTask()
902
- ut.user_id = user.id
917
+ ut.user = user
903
918
  ut.transcription = self.transcription
904
919
  ut.task = self
905
920
  ut.save()
@@ -912,7 +927,7 @@ class Task(TimeStampedModel):
912
927
  # See first comment in claim_transcription about working with the user
913
928
  # instance in these methods.
914
929
  users_usertasks_for_task = (
915
- self.usertasks.filter(user__id=user.id)
930
+ self.usertasks.filter(user=user)
916
931
  .filter(task_type=REVIEW)
917
932
  .exclude(status=FINISHED)
918
933
  )
@@ -927,7 +942,7 @@ class Task(TimeStampedModel):
927
942
  num_tasks = self.project.transcribers_per_task
928
943
  transcriptions = transcriptions[:num_tasks]
929
944
  ut = UserTask()
930
- ut.user_id = user.id
945
+ ut.user = user
931
946
  if len(transcriptions):
932
947
  ut.transcription = self.diff_transcriptions(transcriptions)
933
948
  ut.task = self
@@ -1066,10 +1081,11 @@ class UserTask(TimeStampedModel):
1066
1081
 
1067
1082
  def user_admin_display_name(self):
1068
1083
  params = {
1069
- 'name': self.user.get_full_name(),
1070
- 'netid': self.user.username,
1084
+ 'name': self.user.user.get_full_name(),
1085
+ 'netid': self.user.user.username,
1071
1086
  'url': reverse(
1072
- 'admin:transcribe_transcribeuser_change', args=(self.user.pk,)
1087
+ 'admin:transcribe_transcribeuser_change',
1088
+ args=(self.user.user.pk,),
1073
1089
  ),
1074
1090
  }
1075
1091
  return format_html('<a href="{url}">{name} ({netid})</a>', **params)
transcribe/signals.py CHANGED
@@ -1,14 +1,23 @@
1
1
  """Application Signals"""
2
2
  import logging
3
- from datetime import datetime, timedelta, timezone
4
3
 
5
4
  from django.contrib.auth import get_user_model
6
5
  from django.contrib.auth.signals import user_logged_in
6
+ from django.db.models.signals import post_save
7
+ from django.dispatch import receiver
8
+
9
+ from .models import TranscribeUser
7
10
 
8
11
  logger = logging.getLogger(__file__)
9
12
  User = get_user_model()
10
13
 
11
14
 
15
+ @receiver(post_save, sender=User)
16
+ def manage_user_profile(sender, instance, created, **kwargs):
17
+ if created:
18
+ TranscribeUser.objects.create(user=instance)
19
+
20
+
12
21
  def update_staff_membership(user):
13
22
  """
14
23
  Makes sure the is_staff attribute is set on the user if they are a
@@ -33,7 +33,7 @@
33
33
  <h3>User Reports</h3>
34
34
 
35
35
  <a href="{% url 'reports:users_report' %}?datetime_start={{ previous_week.datetime_start }}&datetime_end={{ previous_week.datetime_end }}">Previous Week, {{ previous_week.week_name }}</a>
36
- <a href="{% url 'reports:projects_report' %}?datetime_start={{ previous_week.datetime_start }}&datetime_end={{ previous_week.datetime_end }}&download=True">(download)</a>
36
+ <a href="{% url 'reports:users_report' %}?datetime_start={{ previous_week.datetime_start }}&datetime_end={{ previous_week.datetime_end }}&download=True">(download)</a>
37
37
  <br>
38
38
  <a href="{% url 'reports:users_report' %}?datetime_start={{ current_month.datetime_start }}&datetime_end={{ current_month.datetime_end }}">{{ current_month.month_name }}</a>
39
39
  <a href="{% url 'reports:users_report' %}?datetime_start={{ current_month.datetime_start }}&datetime_end={{ current_month.datetime_end }}&download=True">(download)</a>
@@ -29,35 +29,16 @@
29
29
  <b>{{ project.title }}</b>
30
30
  <br>
31
31
  Current Status:
32
- <!-- Total Tasks: {{ project.stats.total_tasks }}
33
- <br>
34
- Transcriptions: {{ project.stats.transcriptions_percent_done }}% complete ({{ project.stats.transcriptions_remaining }} tasks remaining)
35
- <br>
36
- Reviews: {{ project.stats.reviews_percent_done }}% complete ({{ project.stats.reviews_remaining }} tasks remaining) -->
37
32
 
38
33
  {{ project.stats.percent_done }}% complete
39
34
  ({{ project.stats.total_finished_transcriptions }} / {{ project.stats.total_tasks }} Transcriptions, {{ project.stats.total_finished_reviews }} / {{ project.stats.total_tasks }} Reviews)
40
35
 
41
- <!-- <br>
42
- <b>
43
- {% if datetime_start|date:'Y' == datetime_end|date:'Y' and datetime_start|date:'M' == datetime_end|date:'M' %}
44
- {{ datetime_start|date:'F j' }} - {{ datetime_end|date:'j, Y' }}
45
- {% elif datetime_start|date:'Y' == datetime_end|date:'Y' %}
46
- {{ datetime_start|date:'F j' }} - {{ datetime_end|date:'F j, Y' }}
47
- {% else %}
48
- {{ datetime_start|date:'M j, Y' }} - {{ datetime_end|date:'M j, Y' }}
49
- {% endif %}:
50
- </b> -->
51
-
52
36
  <br>
53
37
  Finished Transcription{{ project.stats.finished_transcriptions|pluralize }}:
54
38
  {{ project.stats.finished_transcriptions }}
55
- <!-- ({{ project.stats.total_finished_transcriptions }}/{{ project.stats.total_tasks }}) -->
56
39
  <br>
57
40
  Finished Review{{ project.stats.finished_reviews|pluralize }}:
58
41
  {{ project.stats.finished_reviews }}
59
- <!-- ({{ project.stats.total_finished_reviews }}/{{ project.stats.total_tasks }}) -->
60
- <!-- <p>{{ project.stats }}</p> -->
61
42
  </div>
62
43
  {% endfor %}
63
44
 
@@ -16,18 +16,18 @@
16
16
  <a href="{% url 'reports:users_report' %}?datetime_start={{ datetime_start|date:'Y-m-d H:i:s' }}&datetime_end={{ datetime_end|date:'Y-m-d H:i:s' }}&download=True">(download)</a>
17
17
  </h3>
18
18
 
19
- {% for user in users %}
19
+ {% for transcribe_user in transcribe_users %}
20
20
  <p>
21
- <b>{{ user.last_name }}, {{ user.first_name }}</b>
22
- ({{ user }})
21
+ <b>{{ transcribe_user.user.last_name }}, {{ transcribe_user.user.first_name }}</b>
22
+ ({{ transcribe_user }})
23
23
  <br>
24
- Last Login: {{ user.last_login|date:'F j, Y, g:i A' }}
24
+ Last Login: {{ transcribe_user.user.last_login|date:'F j, Y, g:i A' }}
25
25
  <br>
26
- {{ user.num_finished_transcriptions }} finished transcriptions
27
- ({{ user.num_skipped_transcriptions }} skipped, {{ user.num_in_progress_transcriptions }} in progress)
26
+ {{ transcribe_user.num_finished_transcriptions }} finished transcriptions
27
+ ({{ transcribe_user.num_skipped_transcriptions }} skipped, {{ transcribe_user.num_in_progress_transcriptions }} in progress)
28
28
  <br>
29
- {{ user.num_finished_reviews }} finished reviews
30
- ({{ user.num_skipped_reviews }} skipped, {{ user.num_in_progress_reviews }} in progress)
29
+ {{ transcribe_user.num_finished_reviews }} finished reviews
30
+ ({{ transcribe_user.num_skipped_reviews }} skipped, {{ transcribe_user.num_in_progress_reviews }} in progress)
31
31
  </p>
32
32
  {% endfor %}
33
33
 
@@ -6,11 +6,6 @@
6
6
  {% block content %}
7
7
  <h1><span class="icon-user-4"></span> Dashboard</h1>
8
8
 
9
- {% if transcribe_user.is_new %}
10
- <!-- message for new users of Transcribe -->
11
- <!-- <div class='alert'>Welcome to Transcribe.</div> -->
12
- {% endif %}
13
-
14
9
  <section id="available">
15
10
  <header>
16
11
  <h2><span class="icon-checkmark"></span> Available Tasks</h2>
@@ -72,13 +72,11 @@
72
72
  <p>Have a specific question the basic guidelines don't answer? Check the <a href='/faq/'>FAQ page</a>!</p>
73
73
  </div>
74
74
 
75
- <!-- ADMIN STUFF -->
75
+ <!-- ADMIN ITEMS -->
76
76
  {% if transcribe_user.is_admin %}
77
77
  <div class="toolbar">
78
78
  <ul>
79
79
  <li><a href="{% url 'admin:transcribe_project_change' project.id %}" title="Edit this project"><span class="icon-pencil"></span> Edit</a></li>
80
- <!-- <li><a href="/Project/editRoles/{{ project.id }}" title="Mangage user roles for this project (i.e. who is allowed to transcribe it)"><span class="icon-users-2"></span> Roles</a></li> -->
81
-
82
80
  {% if project.tasks %}
83
81
  <li class="message"><span class="icon-download-2"></span> Download:
84
82
  <a href="{% url 'project_download' pk=project.id type='xml' %}">XML</a>
@@ -10,9 +10,6 @@
10
10
  <input class="button" type="submit" value="Save Project">
11
11
 
12
12
  {% if project %}
13
- <!-- <a href="{% url 'project_delete' project.id %}" title="Delete the project" class="button delete" -->
14
- <!-- onClick="if(confirm('Are you sure you want to delete this project?\nThe entire project, transcriptions, and media will be permanently deleted.')) return true; else return false"> -->
15
- <!-- <span class="icon-remove"></span> delete project</a> -->
16
13
  <a href="{% url 'project_delete' project.id %}" title="Delete the project" class="button delete"><span class="icon-remove"></span> delete project</a>
17
14
  {% endif %}
18
15
 
@@ -70,30 +70,31 @@
70
70
  <script src="{% static 'transcribe/js/lib/openseadragon/openseadragon.min.js' %}"></script>
71
71
  <script src="{% static 'transcribe/js/lib/openseadragon/openseadragon-filtering.js' %}"></script>
72
72
  <script type="text/javascript">
73
- var viewer = OpenSeadragon({
73
+ var viewer = OpenSeadragon({
74
74
  id: "openseadragon",
75
75
  prefixUrl: '/static/transcribe/js/lib/openseadragon/images/',
76
76
  tileSources: {
77
77
  type: 'image',
78
- url: '{{ usertask.task.file.url }}',
78
+ url: '{{ usertask.task.file.url }}',
79
+ crossOriginPolicy: 'Anonymous',
79
80
  },
80
81
  // It was decided to load the images full length instead
81
- // of width. This is to allow people to see the whole page
82
- // before beginning transcribing.
82
+ // of width. This is to allow people to see the whole page
83
+ // before beginning transcribing.
83
84
  //visibilityRatio: 1.0, // don't pan beyond image
84
85
  //defaultZoomLevel: 1, // fixed width
85
86
  //minZoomLevel: 1, // can't zoom out more than the full width
86
87
  maxZoomLevel: 5, // can't zoom out more than the full width
87
- showRotationControl: true, // allow image rotation
88
+ showRotationControl: true, // allow image rotation
89
+ });
90
+ viewer.setFilterOptions({
91
+ filters: {
92
+ processors: [
93
+ OpenSeadragon.Filters.BRIGHTNESS(0),
94
+ OpenSeadragon.Filters.CONTRAST(1)
95
+ ]
96
+ }
88
97
  });
89
- viewer.setFilterOptions({
90
- filters: {
91
- processors: [
92
- OpenSeadragon.Filters.BRIGHTNESS(0),
93
- OpenSeadragon.Filters.CONTRAST(1)
94
- ]
95
- }
96
- });
97
98
  function go_to_top(immediately) {
98
99
  var oldBounds = viewer.viewport.getBounds();
99
100
  //var newBounds = new OpenSeadragon.Rect(0, 0, 1, oldBounds.height / oldBounds.width);
@@ -181,20 +182,20 @@
181
182
 
182
183
  <input type="hidden" name="id" value="{{ usertask.id }}" id="id">
183
184
  <input type="hidden" name="task" value="{{ usertask.task.id }}">
184
- <input type="hidden" name="task_type" value="{{ usertask.task_type }}">
185
+ <input type="hidden" name="task_type" value="{{ usertask.task_type }}">
185
186
  <input type="hidden" name="user" value="{{ request.user.id }}">
186
187
  </div>
187
188
 
188
189
  <ul class="toolbar">
189
190
  <li><a id="toggleLayout" href="#"><span class="icon-screen"></span> Toggle Layout</a></li>
190
191
  <li><a class="saveTranscription" href="{{ usertask.task.project.get_absolute_url }}"><span class="icon-info"></span> Project Info</a></li>
191
- <li><span><span class="icon-image"></span> {{ usertask.task.filename }}</span></li>
192
- <li><span id="pagesNav">
193
- <button onclick="return false;" id="previousPageButton" class="icon-arrow-left" title="view the previous page">
194
- <button onclick="return false;" id="currentPageButton" class="icon-home" title="view the page you are currently transcribing">
195
- <button onclick="return false;" id="nextPageButton" class="icon-arrow-right" title="view the next page">
196
- </span></li>
197
- <li><span id="serifToggle" class="serif"><button onclick="return false;" id="serifToggleButton" title="toggle serif font">F</button></span></li>
192
+ <li><span><span class="icon-image"></span> {{ usertask.task.filename }}</span></li>
193
+ <li><span id="pagesNav">
194
+ <button onclick="return false;" id="previousPageButton" class="icon-arrow-left" title="view the previous page">
195
+ <button onclick="return false;" id="currentPageButton" class="icon-home" title="view the page you are currently transcribing">
196
+ <button onclick="return false;" id="nextPageButton" class="icon-arrow-right" title="view the next page">
197
+ </span></li>
198
+ <li><span id="serifToggle" class="serif"><button onclick="return false;" id="serifToggleButton" title="toggle serif font">F</button></span></li>
198
199
  <li class="right finishButton"><button id="finishButton" class="submit" name="status" value="finished" type="submit"><span class="icon-checkmark"></span> Submit</button></li>
199
200
  <li class="right skipButton"><button id="skipButton" class="skip" name="status" value="skipped" type="submit" onClick="if(confirm('Are you sure you want to skip this task?\n')) return true; else return false"><span class="icon-close"></span> Skip</button></li>
200
201
  <li class="right saveButton"><button id="saveButton" class="save" name="status" value="in progress" type="submit"><span class="icon-disk icon"></span><span class="icon-spinner-5 spinner"></span> Save</button></li>
@@ -6,71 +6,16 @@
6
6
  <h1><a href="/users"><span class="icon-users-2"></span> Users</a> &gt; User</h1>
7
7
  </header>
8
8
 
9
- <h2 class='user'>{{ object.name }} ({{ object.username }})</h2>
9
+ <h2 class='user'>{{ transcribe_user.name }} ({{ transcribe_user.user.username }})</h2>
10
10
 
11
11
  <div class='roles'>
12
12
  <h3>Roles</h3>
13
- <!-- $userRoles = $user->getRoles(); -->
14
- <!-- // check for global roles -->
15
- <!-- $globalRoles = array(); -->
16
- <!-- if(isset($userRoles[0])) { -->
17
- <!-- $globalRoles = $userRoles[0]; -->
18
- <!-- // remove global roles, everything else is project-specific -->
19
- <!-- unset($userRoles[0]); -->
20
- <!-- } -->
21
-
22
- <!-- // roles for a specific project or projects -->
23
- <!-- foreach ($userRoles AS $scope => $roles) { -->
24
- <!-- $project = Project::get($scope); -->
25
- <!-- $projectTitle = $project->getTitle(); -->
26
- <!-- echo "<b><a href='/Project/editRoles/" . $project->getId() . "'>$projectTitle</a>: </b>"; -->
27
- <!-- $x = 0; -->
28
- <!-- foreach($roles AS $role) { -->
29
- <!-- $x++; -->
30
- <!-- if($x > 1) echo ", "; -->
31
- <!-- echo "$role"; -->
32
- <!-- } -->
33
- <!-- echo "<br>"; -->
34
- <!-- } -->
35
-
36
- <!-- // global roles, ie roles for All projects -->
37
- <!-- echo "<b class='globalRoles'>Global Roles (All Projects): </b>"; -->
38
- <!-- $x = 0; -->
39
- <!-- foreach($globalRoles AS $role) { -->
40
- <!-- $x++; -->
41
- <!-- if($x > 1) echo ", "; -->
42
- <!-- echo "<a href='/ProjectRole/deleteRole/0/$role/" . $user->getId() . "' class='deleteUser'>"; -->
43
- <!-- echo "$role"; -->
44
- <!-- echo "</a>"; -->
45
- <!-- $global[$role] = true; -->
46
- <!-- } -->
47
- <!-- if(empty($globalRoles)) echo "none"; -->
48
-
49
- <!-- // show options to add global roles for this user? -->
50
- <!-- if(!isset($global['transcriber']) || !isset($global['reviewer']) || !isset($global['admin'])) { -->
51
- <!-- echo "<br>"; -->
52
- <!-- echo "(Add Global Roles: "; -->
53
- <!-- if(!isset($global['transcriber'])) { -->
54
- <!-- echo "<a href='/ProjectRole/addRole/0/transcriber/" . $user->getId() . "' class='addUser'>transcriber</a> "; -->
55
- <!-- } -->
56
- <!-- if(!isset($global['reviewer'])) { -->
57
- <!-- echo "<a href='/ProjectRole/addRole/0/reviewer/" . $user->getId() . "' class='addUser'>reviewer</a> "; -->
58
- <!-- } -->
59
- <!-- if(!isset($global['admin'])) { -->
60
- <!-- echo "<a href='/ProjectRole/addRole/0/admin/" . $user->getId() . "' class='addUser'>admin</a>"; -->
61
- <!-- } -->
62
- <!-- echo ")"; -->
63
- <!-- } -->
64
-
65
- <!-- echo "</div>"; -->
66
13
  </div>
67
-
68
- <!-- $userTasks = $user->getUserTasks('all', 'updatedAt DESC', '100'); -->
69
14
  <br><br>
70
15
  <div class='recentActivity'>
71
16
  <h3>Recent Activity</h3>
72
17
  <ul class='usertasks'>
73
- {% for task in object.recent_transcription_tasks %}
18
+ {% for task in transcribe_user.recent_transcription_tasks %}
74
19
  <li class='{{ task.status }}'>
75
20
  <span class='datetime'>{{ task.modified|date:"M j, Y g:i a" }}</span>
76
21
  <span class='status'>{{ task.status }}</span> task from
@@ -8,28 +8,6 @@
8
8
  <h1><span class="icon-users-2"></span> Users ({{ users.count }})</h1>
9
9
  </header>
10
10
  <span class='recentActivity'>Show statistics for the past: </span>
11
-
12
- <!-- echo " <a href='/users/recentTime:24_hours'"; -->
13
- <!-- if($recentTime == '24 hours') echo " class='current'"; -->
14
- <!-- echo ">24 hours</a>"; -->
15
-
16
- <!-- echo " <a href='/users/recentTime:7_days'"; -->
17
- <!-- if($recentTime == '7 days') echo " class='current'"; -->
18
- <!-- echo ">7 days</a>"; -->
19
-
20
- <!-- echo " <a href='/users/recentTime:1_month'"; -->
21
- <!-- if($recentTime == '1 month') echo " class='current'"; -->
22
- <!-- echo ">1 month</a>"; -->
23
-
24
- <!-- echo " <a href='/users/recentTime:6_months'"; -->
25
- <!-- if($recentTime == '6 months') echo " class='current'"; -->
26
- <!-- echo ">6 months</a>"; -->
27
-
28
- <!-- echo " <a href='/users/recentTime:1_year'"; -->
29
- <!-- if($recentTime == '1 year') echo " class='current'"; -->
30
- <!-- echo ">1 year</a>"; -->
31
- <!-- echo "</span>"; -->
32
-
33
11
  <br/>
34
12
  <br/>
35
13
  <table class="users">
@@ -38,24 +16,20 @@
38
16
  <th>finished</th>
39
17
  <th>skipped</th>
40
18
  <th>in progress</th>
41
- <!-- <th><a href='/users/orderBy:lastName' {% if order_by == 'last_name' %}class='current'{% endif %}>User</a></th> -->
42
- <!-- <th><a href='/users/orderBy:finishedTasks_DESC' {%if order_by == 'finished_tasks desc' %}class='current'{% endif %}>finished</a></th> -->
43
- <!-- <th><a href='/users/orderBy:skippedTasks_DESC'<?php if($orderBy == 'skippedTasks DESC') echo " class='current'"; ?>>skipped</a></th> -->
44
- <!-- <th><a href='/users/orderBy:inProgressTasks_DESC'<?php if($orderBy == 'inProgressTasks DESC') echo " class='current'"; ?>>in progress</a></th> -->
45
19
  </tr>
46
- {% for user in users %}
20
+ {% for transcribe_user in transcribe_users %}
47
21
  <tr>
48
22
  <td>
49
- <a href="/user/{{ user.pk }}">{{ user.sort_name }} ({{ user.username }})</a>
23
+ <a href="/user/{{ transcribe_user.pk }}">{{ transcribe_user.sort_name }} ({{ transcribe_user.user.username }})</a>
50
24
  </td>
51
25
  <td align="center">
52
- {{ user.num_finished_transcriptions }}
26
+ {{ transcribe_user.num_finished_transcriptions }}
53
27
  </td>
54
28
  <td align="center">
55
- {{ user.num_skipped_transcriptions }}
29
+ {{ transcribe_user.num_skipped_transcriptions }}
56
30
  </td>
57
31
  <td align="center">
58
- {{ user.num_in_progress_transcriptions }}
32
+ {{ transcribe_user.num_in_progress_transcriptions }}
59
33
  </td>
60
34
  </tr>
61
35
  {% endfor %}
@@ -74,18 +48,18 @@
74
48
  </tr>
75
49
  <tr>
76
50
  <td>
77
- {% for user in global_transcribers %}
78
- <a href='/user/{{ user.pk }}/'>{{ user.name }} ({{ user.username }})</a><br>
51
+ {% for transcribe_user in global_transcribers %}
52
+ <a href='/user/{{ transcribe_user.pk }}/'>{{ transcribe_user.name }} ({{ transcribe_user.user.username }})</a><br>
79
53
  {% endfor %}
80
54
  </td>
81
55
  <td>
82
- {% for user in global_reviewers %}
83
- <a href='/user/{{ user.pk }}/'>{{ user.name }} ({{ user.username }})</a><br>
56
+ {% for transcribe_user in global_reviewers %}
57
+ <a href='/user/{{ transcribe_user.pk }}/'>{{ transcribe_user.name }} ({{ transcribe_user.user.username }})</a><br>
84
58
  {% endfor %}
85
59
  </td>
86
60
  <td>
87
- {% for user in global_admins %}
88
- <a href='/user/{{ user.pk }}/'>{{ user.name }} ({{ user.username }})</a><br>
61
+ {% for transcribe_user in global_admins %}
62
+ <a href='/user/{{ transcribe_user.pk }}/'>{{ transcribe_user.name }} ({{ transcribe_user.user.username }})</a><br>
89
63
  {% endfor %}
90
64
  </td>
91
65
  </tr>
transcribe/urls.py CHANGED
@@ -1,74 +1,68 @@
1
1
  from django.conf import settings as django_settings
2
- from django.conf.urls import include, static, url
2
+ from django.conf.urls.static import static
3
+ from django.urls import include, path
3
4
  from transcribe.views import reports, web
4
5
 
5
6
  handler404 = 'transcribe.views.web.display404'
6
7
  handler500 = 'transcribe.views.web.display500'
7
8
 
8
9
  project_views = [
9
- url(r'^$', web.landing_page, name='landing_page'),
10
- url(r'^help/$', web.help, name='help'),
11
- url(r'^faq/$', web.faq, name='faq'),
12
- url(r'^dashboard/$', web.DashboardView.as_view(), name='dashboard'),
13
- url(
14
- r'^dashboard/(?P<all_tasks>all_tasks)/$',
10
+ path('', web.landing_page, name='landing_page'),
11
+ path('help/', web.help, name='help'),
12
+ path('faq/', web.faq, name='faq'),
13
+ path('dashboard/', web.DashboardView.as_view(), name='dashboard'),
14
+ path(
15
+ 'dashboard/all_tasks/',
15
16
  web.DashboardView.as_view(),
16
17
  name='dashboard_all_tasks',
18
+ kwargs={'all_tasks': 'all_tasks'},
17
19
  ),
18
- url(r'^projects/$', web.ProjectListView.as_view(), name='projects_list'),
19
- # url(r'^project/create/$', 'project_create', name='project_create'),
20
- url(
21
- r'^project/(?P<pk>\d+)/$',
20
+ path('projects/', web.ProjectListView.as_view(), name='projects_list'),
21
+ path(
22
+ 'project/<int:pk>/',
22
23
  web.ProjectDetailView.as_view(),
23
24
  name='project_detail',
24
25
  ),
25
- # url(r'^project/(?P<pk>\d+)/edit/$', 'project_edit', name='project_edit'),
26
- url(
27
- r'^project/(?P<pk>\d+)/download\.(?P<type>(xml|txt|html))$',
26
+ path(
27
+ 'project/<int:pk>/download.<str:type>',
28
28
  web.ProjectDownloadView.as_view(),
29
29
  name='project_download',
30
30
  ),
31
- # url(r'^project/(?P<pk>\d+)/delete/$', 'project_delete',
32
- # name='project_delete'),
33
- url(
34
- r'^project/(?P<pk>\d+)/claim/(?P<type>(review|transcription|any))/$',
31
+ path(
32
+ 'project/<int:pk>/claim/<str:type>/',
35
33
  web.ProjectClaimTaskView.as_view(),
36
34
  name='project_claim_task',
37
35
  ),
38
- url(
39
- r'^userpreferences/(?P<pk>\d+)/update/$',
36
+ path(
37
+ 'userpreferences/<int:pk>/update/',
40
38
  web.UserPreferencesUpdateView.as_view(),
41
39
  name='user_preferences_update',
42
40
  ),
43
- url(
44
- r'^userprojectpreferences/(?P<pk>\d+)/update/$',
41
+ path(
42
+ 'userprojectpreferences/<int:pk>/update/',
45
43
  web.UserProjectPreferencesUpdateView.as_view(),
46
44
  name='user_project_preferences_update',
47
45
  ),
48
- url(
49
- r'^task/(?P<pk>\d+)/$',
50
- web.UserTaskUpdateView.as_view(),
51
- name='task_workon',
46
+ path(
47
+ 'task/<int:pk>/', web.UserTaskUpdateView.as_view(), name='task_workon'
52
48
  ),
53
49
  ]
54
50
 
55
51
  report_views = (
56
52
  [
57
- url(r'^$', reports.reports_list, name='list'),
58
- url(r'^projects/$', reports.projects_report, name='projects_report'),
59
- url(r'^users/$', reports.users_report, name='users_report'),
53
+ path('', reports.reports_list, name='list'),
54
+ path('projects/', reports.projects_report, name='projects_report'),
55
+ path('users/', reports.users_report, name='users_report'),
60
56
  ],
61
57
  'reports',
62
58
  )
63
59
 
64
60
  urlpatterns = [
65
- # url(r'^', include(authentication_views)),
66
- url(r'^', include(project_views)),
67
- url(r'^reports/', include(report_views)),
61
+ path('', include(project_views)),
62
+ path('reports/', include(report_views)),
68
63
  ]
69
64
 
70
65
  if django_settings.DEBUG:
71
- static = static.static
72
66
  urlpatterns += static(
73
67
  django_settings.STATIC_URL, document_root=django_settings.STATIC_ROOT
74
68
  )
@@ -83,6 +83,6 @@ class TranscribeUserContextMixin:
83
83
  if hasattr(super(), 'get_context_data'):
84
84
  context = super().get_context_data(**kwargs)
85
85
  context['transcribe_user'] = TranscribeUser.objects.get(
86
- pk=self.request.user.pk
86
+ user=self.request.user
87
87
  )
88
88
  return context
@@ -262,25 +262,27 @@ def users_report(request):
262
262
  datetime_end = make_aware(datetime_end)
263
263
 
264
264
  # list of users
265
- users = TranscribeUser.objects.filter(last_login__gte=datetime_start)
265
+ transcribe_users = TranscribeUser.objects.filter(
266
+ user__last_login__gte=datetime_start
267
+ )
266
268
  # get stats for each user
267
- for user in users:
268
- user._get_report_stats(datetime_start, datetime_end)
269
+ for transcribe_user in transcribe_users:
270
+ transcribe_user._get_report_stats(datetime_start, datetime_end)
269
271
  # remove users without any activity
270
272
  active_users = []
271
- for user in users:
273
+ for transcribe_user in transcribe_users:
272
274
  if (
273
- user.num_finished_transcriptions > 0
274
- or user.num_finished_reviews > 0
275
- or user.num_skipped_transcriptions > 0
276
- or user.num_skipped_reviews > 0
275
+ transcribe_user.num_finished_transcriptions > 0
276
+ or transcribe_user.num_finished_reviews > 0
277
+ or transcribe_user.num_skipped_transcriptions > 0
278
+ or transcribe_user.num_skipped_reviews > 0
277
279
  ):
278
- active_users.append(user)
279
- users = []
280
+ active_users.append(transcribe_user)
281
+ transcribe_users = []
280
282
  data = {
281
283
  'datetime_start': datetime_start,
282
284
  'datetime_end': datetime_end,
283
- 'users': active_users,
285
+ 'transcribe_users': active_users,
284
286
  }
285
287
 
286
288
  # download the report as a csv file
@@ -311,19 +313,23 @@ def users_report(request):
311
313
  ]
312
314
  )
313
315
  # stats for each user
314
- for user in active_users:
316
+ for transcribe_user in active_users:
315
317
  user_data = []
316
- user_data.append(f'{user.last_name}, {user.first_name}')
317
- user_data.append(f'{user}')
318
318
  user_data.append(
319
- f"{user.last_login.strftime('%B %-d, %Y, %-I:%M %p')}"
319
+ f'{transcribe_user.user.last_name}, {transcribe_user.user.first_name}'
320
+ )
321
+ user_data.append(f'{transcribe_user}')
322
+ user_data.append(
323
+ f"{transcribe_user.user.last_login.strftime('%B %-d, %Y, %-I:%M %p')}"
324
+ )
325
+ user_data.append(f'{transcribe_user.num_finished_transcriptions}')
326
+ user_data.append(f'{transcribe_user.num_skipped_transcriptions}')
327
+ user_data.append(
328
+ f'{transcribe_user.num_in_progress_transcriptions}'
320
329
  )
321
- user_data.append(f'{user.num_finished_transcriptions}')
322
- user_data.append(f'{user.num_skipped_transcriptions}')
323
- user_data.append(f'{user.num_in_progress_transcriptions}')
324
- user_data.append(f'{user.num_finished_reviews}')
325
- user_data.append(f'{user.num_skipped_reviews}')
326
- user_data.append(f'{user.num_in_progress_reviews}')
330
+ user_data.append(f'{transcribe_user.num_finished_reviews}')
331
+ user_data.append(f'{transcribe_user.num_skipped_reviews}')
332
+ user_data.append(f'{transcribe_user.num_in_progress_reviews}')
327
333
  writer.writerow(user_data)
328
334
  # totals
329
335
  writer.writerow(['', ''])
@@ -333,15 +339,21 @@ def users_report(request):
333
339
  total_skipped_reviews = 0
334
340
  total_in_progress_transcriptions = 0
335
341
  total_in_progress_reviews = 0
336
- for user in active_users:
337
- total_finished_transcriptions += user.num_finished_transcriptions
338
- total_skipped_transcriptions += user.num_skipped_transcriptions
339
- total_finished_reviews += user.num_finished_reviews
340
- total_skipped_reviews += user.num_skipped_reviews
342
+ for transcribe_user in active_users:
343
+ total_finished_transcriptions += (
344
+ transcribe_user.num_finished_transcriptions
345
+ )
346
+ total_skipped_transcriptions += (
347
+ transcribe_user.num_skipped_transcriptions
348
+ )
349
+ total_finished_reviews += transcribe_user.num_finished_reviews
350
+ total_skipped_reviews += transcribe_user.num_skipped_reviews
341
351
  total_in_progress_transcriptions += (
342
- user.num_in_progress_transcriptions
352
+ transcribe_user.num_in_progress_transcriptions
353
+ )
354
+ total_in_progress_reviews += (
355
+ transcribe_user.num_in_progress_reviews
343
356
  )
344
- total_in_progress_reviews += user.num_in_progress_reviews
345
357
  writer.writerow(
346
358
  [
347
359
  'TOTALS',
transcribe/views/web.py CHANGED
@@ -4,6 +4,7 @@ from django.contrib import messages
4
4
  from django.contrib.auth.decorators import login_required
5
5
  from django.db.models import Count, Q
6
6
  from django.http import HttpResponse, JsonResponse
7
+ from django.http.response import Http404
7
8
  from django.shortcuts import redirect, render
8
9
  from django.views.generic import View
9
10
  from django.views.generic.detail import DetailView
@@ -90,9 +91,11 @@ def display500(request):
90
91
  class DashboardView(
91
92
  mixins.ActiveUsersOnlyMixin, mixins.TranscribeUserContextMixin, View
92
93
  ):
93
- def get(self, request, all_tasks=False): # noqa
94
+ def get( # noqa C901, TODO simplify this method
95
+ self, request, *args, **kwargs
96
+ ):
97
+ all_tasks = kwargs.get('all_tasks', False)
94
98
  context = super().get_context_data()
95
-
96
99
  context['projects'] = []
97
100
  user = context['transcribe_user']
98
101
 
@@ -105,7 +108,7 @@ class DashboardView(
105
108
  # 'in progress' tasks for this user
106
109
  user_tasks_in_progress = (
107
110
  models.UserTask.objects.defer('transcription')
108
- .filter(status='in progress', user_id=user.pk)
111
+ .filter(status='in progress', user=user)
109
112
  .select_related('task__project')
110
113
  .all()
111
114
  )
@@ -190,9 +193,12 @@ class ProjectClaimTaskView(mixins.ActiveUsersOnlyMixin, View):
190
193
  Creates a UserTask for a given project and user, then redirects to the
191
194
  UserTask page or the dashboard if something failed.
192
195
  """
196
+ if type not in {'transcription', 'review', 'any'}:
197
+ raise Http404
198
+
193
199
  failed = False
194
200
  user_task = None
195
- user = models.TranscribeUser.objects.get(pk=request.user.pk)
201
+ user = models.TranscribeUser.objects.get(user=request.user)
196
202
 
197
203
  try:
198
204
  project = models.Project.objects.get(pk=pk)
@@ -264,6 +270,11 @@ class ProjectDownloadView(mixins.AdminUsersOnlyMixin, View):
264
270
  elif type == 'html':
265
271
  result = project.generate_html()
266
272
  content_type = 'text/html'
273
+ else:
274
+ messages.add_message(
275
+ request, messages.ERROR, 'Invalid export type.'
276
+ )
277
+ return redirect('dashboard')
267
278
  return HttpResponse(result, content_type=content_type)
268
279
 
269
280