We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
cvs export of group documents is dumping b'' strings
From Tim Wicinski:
>FYI
>
>So I went and dumped the WG documents via the CSV link:
>
>https://datatracker.ietf.org/group/dnsop/documents/csv/
>
>Then I imported them into the googles sheets.
>The CSV file has all strings as "b'<string>'" which
>makes the import needing a bit of cleanup. 
>
>tim

cc @moonshiner


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ietf/community/views.py`
Content:
```
1 # Copyright The IETF Trust 2012-2020, All Rights Reserved
2 # -*- coding: utf-8 -*-
3 
4 
5 import csv
6 import datetime
7 import json
8 import uuid
9 
10 from django.http import HttpResponse, HttpResponseRedirect, Http404
11 from django.shortcuts import get_object_or_404, render
12 from django.contrib.auth.decorators import login_required
13 from django.utils.html import strip_tags
14 
15 import debug                            # pyflakes:ignore
16 
17 from ietf.community.models import SearchRule, EmailSubscription
18 from ietf.community.forms import SearchRuleTypeForm, SearchRuleForm, AddDocumentsForm, SubscriptionForm
19 from ietf.community.utils import lookup_community_list, can_manage_community_list
20 from ietf.community.utils import docs_tracked_by_community_list, docs_matching_community_list_rule
21 from ietf.community.utils import states_of_significant_change, reset_name_contains_index_for_rule
22 from ietf.doc.models import DocEvent, Document
23 from ietf.doc.utils_search import prepare_document_table
24 from ietf.utils.response import permission_denied
25 
26 def view_list(request, username=None):
27     clist = lookup_community_list(username)
28 
29     docs = docs_tracked_by_community_list(clist)
30     docs, meta = prepare_document_table(request, docs, request.GET)
31 
32     subscribed = request.user.is_authenticated and EmailSubscription.objects.filter(community_list=clist, email__person__user=request.user)
33 
34     return render(request, 'community/view_list.html', {
35         'clist': clist,
36         'docs': docs,
37         'meta': meta,
38         'can_manage_list': can_manage_community_list(request.user, clist),
39         'subscribed': subscribed,
40     })
41 
42 @login_required
43 def manage_list(request, username=None, acronym=None, group_type=None):
44     # we need to be a bit careful because clist may not exist in the
45     # database so we can't call related stuff on it yet
46     clist = lookup_community_list(username, acronym)
47 
48     if not can_manage_community_list(request.user, clist):
49         permission_denied(request, "You do not have permission to access this view")
50 
51     action = request.POST.get('action')
52 
53     if request.method == 'POST' and action == 'add_documents':
54         add_doc_form = AddDocumentsForm(request.POST)
55         if add_doc_form.is_valid():
56             if clist.pk is None:
57                 clist.save()
58 
59             for d in add_doc_form.cleaned_data['documents']:
60                 if not d in clist.added_docs.all():
61                     clist.added_docs.add(d)
62 
63             return HttpResponseRedirect("")
64     else:
65         add_doc_form = AddDocumentsForm()
66 
67     if request.method == 'POST' and action == 'remove_document':
68         document_id = request.POST.get('document')
69         if clist.pk is not None and document_id:
70             document = get_object_or_404(clist.added_docs, id=document_id)
71             clist.added_docs.remove(document)
72 
73             return HttpResponseRedirect("")
74 
75     rule_form = None
76     if request.method == 'POST' and action == 'add_rule':
77         rule_type_form = SearchRuleTypeForm(request.POST)
78         if rule_type_form.is_valid():
79             rule_type = rule_type_form.cleaned_data['rule_type']
80 
81         if rule_type:
82             rule_form = SearchRuleForm(clist, rule_type, request.POST)
83             if rule_form.is_valid():
84                 if clist.pk is None:
85                     clist.save()
86 
87                 rule = rule_form.save(commit=False)
88                 rule.community_list = clist
89                 rule.rule_type = rule_type
90                 rule.save()
91                 if rule.rule_type == "name_contains":
92                     reset_name_contains_index_for_rule(rule)
93 
94                 return HttpResponseRedirect("")
95     else:
96         rule_type_form = SearchRuleTypeForm()
97 
98     if request.method == 'POST' and action == 'remove_rule':
99         rule_pk = request.POST.get('rule')
100         if clist.pk is not None and rule_pk:
101             rule = get_object_or_404(SearchRule, pk=rule_pk, community_list=clist)
102             rule.delete()
103 
104         return HttpResponseRedirect("")
105 
106     rules = clist.searchrule_set.all() if clist.pk is not None else []
107     for r in rules:
108         r.matching_documents_count = docs_matching_community_list_rule(r).count()
109 
110     empty_rule_forms = { rule_type: SearchRuleForm(clist, rule_type) for rule_type, _ in SearchRule.RULE_TYPES }
111 
112     total_count = docs_tracked_by_community_list(clist).count()
113 
114     all_forms = [f for f in [rule_type_form, rule_form, add_doc_form, *empty_rule_forms.values()]
115                  if f is not None]
116     return render(request, 'community/manage_list.html', {
117         'clist': clist,
118         'rules': rules,
119         'individually_added': clist.added_docs.all() if clist.pk is not None else [],
120         'rule_type_form': rule_type_form,
121         'rule_form': rule_form,
122         'empty_rule_forms': empty_rule_forms,
123         'total_count': total_count,
124         'add_doc_form': add_doc_form,
125         'all_forms': all_forms,
126     })
127 
128 
129 @login_required
130 def track_document(request, name, username=None, acronym=None):
131     doc = get_object_or_404(Document, docalias__name=name)
132 
133     if request.method == "POST":
134         clist = lookup_community_list(username, acronym)
135         if not can_manage_community_list(request.user, clist):
136             permission_denied(request, "You do not have permission to access this view")
137 
138         if clist.pk is None:
139             clist.save()
140 
141         if not doc in clist.added_docs.all():
142             clist.added_docs.add(doc)
143 
144         if request.is_ajax():
145             return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')
146         else:
147             return HttpResponseRedirect(clist.get_absolute_url())
148 
149     return render(request, "community/track_document.html", {
150         "name": doc.name,
151     })
152 
153 @login_required
154 def untrack_document(request, name, username=None, acronym=None):
155     doc = get_object_or_404(Document, docalias__name=name)
156     clist = lookup_community_list(username, acronym)
157     if not can_manage_community_list(request.user, clist):
158         permission_denied(request, "You do not have permission to access this view")
159 
160     if request.method == "POST":
161         if clist.pk is not None:
162             clist.added_docs.remove(doc)
163 
164         if request.is_ajax():
165             return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')
166         else:
167             return HttpResponseRedirect(clist.get_absolute_url())
168 
169     return render(request, "community/untrack_document.html", {
170         "name": doc.name,
171     })
172 
173 
174 def export_to_csv(request, username=None, acronym=None, group_type=None):
175     clist = lookup_community_list(username, acronym)
176 
177     response = HttpResponse(content_type='text/csv')
178 
179     if clist.group:
180         filename = "%s-draft-list.csv" % clist.group.acronym
181     else:
182         filename = "draft-list.csv"
183 
184     response['Content-Disposition'] = 'attachment; filename=%s' % filename
185 
186     writer = csv.writer(response, dialect=csv.excel, delimiter=str(','))
187 
188     header = [
189         "Name",
190         "Title",
191         "Date of latest revision",
192         "Status in the IETF process",
193         "Associated group",
194         "Associated AD",
195         "Date of latest change",
196     ]
197     writer.writerow(header)
198 
199     docs = docs_tracked_by_community_list(clist).select_related('type', 'group', 'ad')
200     for doc in docs.prefetch_related("states", "tags"):
201         row = []
202         row.append(doc.name)
203         row.append(doc.title)
204         e = doc.latest_event(type='new_revision')
205         row.append(e.time.strftime("%Y-%m-%d") if e else "")
206         row.append(strip_tags(doc.friendly_state()))
207         row.append(doc.group.acronym if doc.group else "")
208         row.append(str(doc.ad) if doc.ad else "")
209         e = doc.latest_event()
210         row.append(e.time.strftime("%Y-%m-%d") if e else "")
211         writer.writerow([v.encode("utf-8") for v in row])
212 
213     return response
214 
215 def feed(request, username=None, acronym=None, group_type=None):
216     clist = lookup_community_list(username, acronym)
217 
218     significant = request.GET.get('significant', '') == '1'
219 
220     documents = docs_tracked_by_community_list(clist).values_list('pk', flat=True)
221     since = datetime.datetime.now() - datetime.timedelta(days=14)
222 
223     events = DocEvent.objects.filter(
224         doc__id__in=documents,
225         time__gte=since,
226     ).distinct().order_by('-time', '-id').select_related("doc")
227 
228     if significant:
229         events = events.filter(type="changed_state", statedocevent__state__in=list(states_of_significant_change()))
230 
231     host = request.get_host()
232     feed_url = 'https://%s%s' % (host, request.get_full_path())
233     feed_id = uuid.uuid5(uuid.NAMESPACE_URL, str(feed_url))
234     title = '%s RSS Feed' % clist.long_name()
235     if significant:
236         subtitle = 'Significant document changes'
237     else:
238         subtitle = 'Document changes'
239 
240     return render(request, 'community/atom.xml', {
241         'clist': clist,
242         'entries': events[:50],
243         'title': title,
244         'subtitle': subtitle,
245         'id': feed_id.urn,
246         'updated': datetime.datetime.now(),
247     }, content_type='text/xml')
248 
249 
250 @login_required
251 def subscription(request, username=None, acronym=None, group_type=None):
252     clist = lookup_community_list(username, acronym)
253     if clist.pk is None:
254         raise Http404
255 
256     existing_subscriptions = EmailSubscription.objects.filter(community_list=clist, email__person__user=request.user)
257 
258     if request.method == 'POST':
259         action = request.POST.get("action")
260         if action == "subscribe":
261             form = SubscriptionForm(request.user, clist, request.POST)
262             if form.is_valid():
263                 subscription = form.save(commit=False)
264                 subscription.community_list = clist
265                 subscription.save()
266 
267                 return HttpResponseRedirect("")
268 
269         elif action == "unsubscribe":
270             existing_subscriptions.filter(pk=request.POST.get("subscription_id")).delete()
271 
272             return HttpResponseRedirect("")
273     else:
274         form = SubscriptionForm(request.user, clist)
275 
276     return render(request, 'community/subscription.html', {
277         'clist': clist,
278         'form': form,
279         'existing_subscriptions': existing_subscriptions,
280     })
281
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ietf/community/views.py b/ietf/community/views.py
--- a/ietf/community/views.py
+++ b/ietf/community/views.py
@@ -208,7 +208,7 @@
         row.append(str(doc.ad) if doc.ad else "")
         e = doc.latest_event()
         row.append(e.time.strftime("%Y-%m-%d") if e else "")
-        writer.writerow([v.encode("utf-8") for v in row])
+        writer.writerow(row)
 
     return response

{"golden_diff": "diff --git a/ietf/community/views.py b/ietf/community/views.py\n--- a/ietf/community/views.py\n+++ b/ietf/community/views.py\n@@ -208,7 +208,7 @@\n         row.append(str(doc.ad) if doc.ad else \"\")\n         e = doc.latest_event()\n         row.append(e.time.strftime(\"%Y-%m-%d\") if e else \"\")\n-        writer.writerow([v.encode(\"utf-8\") for v in row])\n+        writer.writerow(row)\n \n     return response\n", "issue": "cvs export of group documents is dumping b'' strings\nFrom Tim Wicinski:\r\n>FYI\r\n>\r\n>So I went and dumped the WG documents via the CSV link:\r\n>\r\n>https://datatracker.ietf.org/group/dnsop/documents/csv/\r\n>\r\n>Then I imported them into the googles sheets.\r\n>The CSV file has all strings as \"b'<string>'\" which\r\n>makes the import needing a bit of cleanup. \r\n>\r\n>tim\r\n\r\ncc @moonshiner\r\n\n", "before_files": [{"content": "# Copyright The IETF Trust 2012-2020, All Rights Reserved\n# -*- coding: utf-8 -*-\n\n\nimport csv\nimport datetime\nimport json\nimport uuid\n\nfrom django.http import HttpResponse, HttpResponseRedirect, Http404\nfrom django.shortcuts import get_object_or_404, render\nfrom django.contrib.auth.decorators import login_required\nfrom django.utils.html import strip_tags\n\nimport debug                            # pyflakes:ignore\n\nfrom ietf.community.models import SearchRule, EmailSubscription\nfrom ietf.community.forms import SearchRuleTypeForm, SearchRuleForm, AddDocumentsForm, SubscriptionForm\nfrom ietf.community.utils import lookup_community_list, can_manage_community_list\nfrom ietf.community.utils import docs_tracked_by_community_list, docs_matching_community_list_rule\nfrom ietf.community.utils import states_of_significant_change, reset_name_contains_index_for_rule\nfrom ietf.doc.models import DocEvent, Document\nfrom ietf.doc.utils_search import prepare_document_table\nfrom ietf.utils.response import permission_denied\n\ndef view_list(request, username=None):\n    clist = lookup_community_list(username)\n\n    docs = docs_tracked_by_community_list(clist)\n    docs, meta = prepare_document_table(request, docs, request.GET)\n\n    subscribed = request.user.is_authenticated and EmailSubscription.objects.filter(community_list=clist, email__person__user=request.user)\n\n    return render(request, 'community/view_list.html', {\n        'clist': clist,\n        'docs': docs,\n        'meta': meta,\n        'can_manage_list': can_manage_community_list(request.user, clist),\n        'subscribed': subscribed,\n    })\n\n@login_required\ndef manage_list(request, username=None, acronym=None, group_type=None):\n    # we need to be a bit careful because clist may not exist in the\n    # database so we can't call related stuff on it yet\n    clist = lookup_community_list(username, acronym)\n\n    if not can_manage_community_list(request.user, clist):\n        permission_denied(request, \"You do not have permission to access this view\")\n\n    action = request.POST.get('action')\n\n    if request.method == 'POST' and action == 'add_documents':\n        add_doc_form = AddDocumentsForm(request.POST)\n        if add_doc_form.is_valid():\n            if clist.pk is None:\n                clist.save()\n\n            for d in add_doc_form.cleaned_data['documents']:\n                if not d in clist.added_docs.all():\n                    clist.added_docs.add(d)\n\n            return HttpResponseRedirect(\"\")\n    else:\n        add_doc_form = AddDocumentsForm()\n\n    if request.method == 'POST' and action == 'remove_document':\n        document_id = request.POST.get('document')\n        if clist.pk is not None and document_id:\n            document = get_object_or_404(clist.added_docs, id=document_id)\n            clist.added_docs.remove(document)\n\n            return HttpResponseRedirect(\"\")\n\n    rule_form = None\n    if request.method == 'POST' and action == 'add_rule':\n        rule_type_form = SearchRuleTypeForm(request.POST)\n        if rule_type_form.is_valid():\n            rule_type = rule_type_form.cleaned_data['rule_type']\n\n        if rule_type:\n            rule_form = SearchRuleForm(clist, rule_type, request.POST)\n            if rule_form.is_valid():\n                if clist.pk is None:\n                    clist.save()\n\n                rule = rule_form.save(commit=False)\n                rule.community_list = clist\n                rule.rule_type = rule_type\n                rule.save()\n                if rule.rule_type == \"name_contains\":\n                    reset_name_contains_index_for_rule(rule)\n\n                return HttpResponseRedirect(\"\")\n    else:\n        rule_type_form = SearchRuleTypeForm()\n\n    if request.method == 'POST' and action == 'remove_rule':\n        rule_pk = request.POST.get('rule')\n        if clist.pk is not None and rule_pk:\n            rule = get_object_or_404(SearchRule, pk=rule_pk, community_list=clist)\n            rule.delete()\n\n        return HttpResponseRedirect(\"\")\n\n    rules = clist.searchrule_set.all() if clist.pk is not None else []\n    for r in rules:\n        r.matching_documents_count = docs_matching_community_list_rule(r).count()\n\n    empty_rule_forms = { rule_type: SearchRuleForm(clist, rule_type) for rule_type, _ in SearchRule.RULE_TYPES }\n\n    total_count = docs_tracked_by_community_list(clist).count()\n\n    all_forms = [f for f in [rule_type_form, rule_form, add_doc_form, *empty_rule_forms.values()]\n                 if f is not None]\n    return render(request, 'community/manage_list.html', {\n        'clist': clist,\n        'rules': rules,\n        'individually_added': clist.added_docs.all() if clist.pk is not None else [],\n        'rule_type_form': rule_type_form,\n        'rule_form': rule_form,\n        'empty_rule_forms': empty_rule_forms,\n        'total_count': total_count,\n        'add_doc_form': add_doc_form,\n        'all_forms': all_forms,\n    })\n\n\n@login_required\ndef track_document(request, name, username=None, acronym=None):\n    doc = get_object_or_404(Document, docalias__name=name)\n\n    if request.method == \"POST\":\n        clist = lookup_community_list(username, acronym)\n        if not can_manage_community_list(request.user, clist):\n            permission_denied(request, \"You do not have permission to access this view\")\n\n        if clist.pk is None:\n            clist.save()\n\n        if not doc in clist.added_docs.all():\n            clist.added_docs.add(doc)\n\n        if request.is_ajax():\n            return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')\n        else:\n            return HttpResponseRedirect(clist.get_absolute_url())\n\n    return render(request, \"community/track_document.html\", {\n        \"name\": doc.name,\n    })\n\n@login_required\ndef untrack_document(request, name, username=None, acronym=None):\n    doc = get_object_or_404(Document, docalias__name=name)\n    clist = lookup_community_list(username, acronym)\n    if not can_manage_community_list(request.user, clist):\n        permission_denied(request, \"You do not have permission to access this view\")\n\n    if request.method == \"POST\":\n        if clist.pk is not None:\n            clist.added_docs.remove(doc)\n\n        if request.is_ajax():\n            return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')\n        else:\n            return HttpResponseRedirect(clist.get_absolute_url())\n\n    return render(request, \"community/untrack_document.html\", {\n        \"name\": doc.name,\n    })\n\n\ndef export_to_csv(request, username=None, acronym=None, group_type=None):\n    clist = lookup_community_list(username, acronym)\n\n    response = HttpResponse(content_type='text/csv')\n\n    if clist.group:\n        filename = \"%s-draft-list.csv\" % clist.group.acronym\n    else:\n        filename = \"draft-list.csv\"\n\n    response['Content-Disposition'] = 'attachment; filename=%s' % filename\n\n    writer = csv.writer(response, dialect=csv.excel, delimiter=str(','))\n\n    header = [\n        \"Name\",\n        \"Title\",\n        \"Date of latest revision\",\n        \"Status in the IETF process\",\n        \"Associated group\",\n        \"Associated AD\",\n        \"Date of latest change\",\n    ]\n    writer.writerow(header)\n\n    docs = docs_tracked_by_community_list(clist).select_related('type', 'group', 'ad')\n    for doc in docs.prefetch_related(\"states\", \"tags\"):\n        row = []\n        row.append(doc.name)\n        row.append(doc.title)\n        e = doc.latest_event(type='new_revision')\n        row.append(e.time.strftime(\"%Y-%m-%d\") if e else \"\")\n        row.append(strip_tags(doc.friendly_state()))\n        row.append(doc.group.acronym if doc.group else \"\")\n        row.append(str(doc.ad) if doc.ad else \"\")\n        e = doc.latest_event()\n        row.append(e.time.strftime(\"%Y-%m-%d\") if e else \"\")\n        writer.writerow([v.encode(\"utf-8\") for v in row])\n\n    return response\n\ndef feed(request, username=None, acronym=None, group_type=None):\n    clist = lookup_community_list(username, acronym)\n\n    significant = request.GET.get('significant', '') == '1'\n\n    documents = docs_tracked_by_community_list(clist).values_list('pk', flat=True)\n    since = datetime.datetime.now() - datetime.timedelta(days=14)\n\n    events = DocEvent.objects.filter(\n        doc__id__in=documents,\n        time__gte=since,\n    ).distinct().order_by('-time', '-id').select_related(\"doc\")\n\n    if significant:\n        events = events.filter(type=\"changed_state\", statedocevent__state__in=list(states_of_significant_change()))\n\n    host = request.get_host()\n    feed_url = 'https://%s%s' % (host, request.get_full_path())\n    feed_id = uuid.uuid5(uuid.NAMESPACE_URL, str(feed_url))\n    title = '%s RSS Feed' % clist.long_name()\n    if significant:\n        subtitle = 'Significant document changes'\n    else:\n        subtitle = 'Document changes'\n\n    return render(request, 'community/atom.xml', {\n        'clist': clist,\n        'entries': events[:50],\n        'title': title,\n        'subtitle': subtitle,\n        'id': feed_id.urn,\n        'updated': datetime.datetime.now(),\n    }, content_type='text/xml')\n\n\n@login_required\ndef subscription(request, username=None, acronym=None, group_type=None):\n    clist = lookup_community_list(username, acronym)\n    if clist.pk is None:\n        raise Http404\n\n    existing_subscriptions = EmailSubscription.objects.filter(community_list=clist, email__person__user=request.user)\n\n    if request.method == 'POST':\n        action = request.POST.get(\"action\")\n        if action == \"subscribe\":\n            form = SubscriptionForm(request.user, clist, request.POST)\n            if form.is_valid():\n                subscription = form.save(commit=False)\n                subscription.community_list = clist\n                subscription.save()\n\n                return HttpResponseRedirect(\"\")\n\n        elif action == \"unsubscribe\":\n            existing_subscriptions.filter(pk=request.POST.get(\"subscription_id\")).delete()\n\n            return HttpResponseRedirect(\"\")\n    else:\n        form = SubscriptionForm(request.user, clist)\n\n    return render(request, 'community/subscription.html', {\n        'clist': clist,\n        'form': form,\n        'existing_subscriptions': existing_subscriptions,\n    })\n", "path": "ietf/community/views.py"}], "after_files": [{"content": "# Copyright The IETF Trust 2012-2020, All Rights Reserved\n# -*- coding: utf-8 -*-\n\n\nimport csv\nimport datetime\nimport json\nimport uuid\n\nfrom django.http import HttpResponse, HttpResponseRedirect, Http404\nfrom django.shortcuts import get_object_or_404, render\nfrom django.contrib.auth.decorators import login_required\nfrom django.utils.html import strip_tags\n\nimport debug                            # pyflakes:ignore\n\nfrom ietf.community.models import SearchRule, EmailSubscription\nfrom ietf.community.forms import SearchRuleTypeForm, SearchRuleForm, AddDocumentsForm, SubscriptionForm\nfrom ietf.community.utils import lookup_community_list, can_manage_community_list\nfrom ietf.community.utils import docs_tracked_by_community_list, docs_matching_community_list_rule\nfrom ietf.community.utils import states_of_significant_change, reset_name_contains_index_for_rule\nfrom ietf.doc.models import DocEvent, Document\nfrom ietf.doc.utils_search import prepare_document_table\nfrom ietf.utils.response import permission_denied\n\ndef view_list(request, username=None):\n    clist = lookup_community_list(username)\n\n    docs = docs_tracked_by_community_list(clist)\n    docs, meta = prepare_document_table(request, docs, request.GET)\n\n    subscribed = request.user.is_authenticated and EmailSubscription.objects.filter(community_list=clist, email__person__user=request.user)\n\n    return render(request, 'community/view_list.html', {\n        'clist': clist,\n        'docs': docs,\n        'meta': meta,\n        'can_manage_list': can_manage_community_list(request.user, clist),\n        'subscribed': subscribed,\n    })\n\n@login_required\ndef manage_list(request, username=None, acronym=None, group_type=None):\n    # we need to be a bit careful because clist may not exist in the\n    # database so we can't call related stuff on it yet\n    clist = lookup_community_list(username, acronym)\n\n    if not can_manage_community_list(request.user, clist):\n        permission_denied(request, \"You do not have permission to access this view\")\n\n    action = request.POST.get('action')\n\n    if request.method == 'POST' and action == 'add_documents':\n        add_doc_form = AddDocumentsForm(request.POST)\n        if add_doc_form.is_valid():\n            if clist.pk is None:\n                clist.save()\n\n            for d in add_doc_form.cleaned_data['documents']:\n                if not d in clist.added_docs.all():\n                    clist.added_docs.add(d)\n\n            return HttpResponseRedirect(\"\")\n    else:\n        add_doc_form = AddDocumentsForm()\n\n    if request.method == 'POST' and action == 'remove_document':\n        document_id = request.POST.get('document')\n        if clist.pk is not None and document_id:\n            document = get_object_or_404(clist.added_docs, id=document_id)\n            clist.added_docs.remove(document)\n\n            return HttpResponseRedirect(\"\")\n\n    rule_form = None\n    if request.method == 'POST' and action == 'add_rule':\n        rule_type_form = SearchRuleTypeForm(request.POST)\n        if rule_type_form.is_valid():\n            rule_type = rule_type_form.cleaned_data['rule_type']\n\n        if rule_type:\n            rule_form = SearchRuleForm(clist, rule_type, request.POST)\n            if rule_form.is_valid():\n                if clist.pk is None:\n                    clist.save()\n\n                rule = rule_form.save(commit=False)\n                rule.community_list = clist\n                rule.rule_type = rule_type\n                rule.save()\n                if rule.rule_type == \"name_contains\":\n                    reset_name_contains_index_for_rule(rule)\n\n                return HttpResponseRedirect(\"\")\n    else:\n        rule_type_form = SearchRuleTypeForm()\n\n    if request.method == 'POST' and action == 'remove_rule':\n        rule_pk = request.POST.get('rule')\n        if clist.pk is not None and rule_pk:\n            rule = get_object_or_404(SearchRule, pk=rule_pk, community_list=clist)\n            rule.delete()\n\n        return HttpResponseRedirect(\"\")\n\n    rules = clist.searchrule_set.all() if clist.pk is not None else []\n    for r in rules:\n        r.matching_documents_count = docs_matching_community_list_rule(r).count()\n\n    empty_rule_forms = { rule_type: SearchRuleForm(clist, rule_type) for rule_type, _ in SearchRule.RULE_TYPES }\n\n    total_count = docs_tracked_by_community_list(clist).count()\n\n    all_forms = [f for f in [rule_type_form, rule_form, add_doc_form, *empty_rule_forms.values()]\n                 if f is not None]\n    return render(request, 'community/manage_list.html', {\n        'clist': clist,\n        'rules': rules,\n        'individually_added': clist.added_docs.all() if clist.pk is not None else [],\n        'rule_type_form': rule_type_form,\n        'rule_form': rule_form,\n        'empty_rule_forms': empty_rule_forms,\n        'total_count': total_count,\n        'add_doc_form': add_doc_form,\n        'all_forms': all_forms,\n    })\n\n\n@login_required\ndef track_document(request, name, username=None, acronym=None):\n    doc = get_object_or_404(Document, docalias__name=name)\n\n    if request.method == \"POST\":\n        clist = lookup_community_list(username, acronym)\n        if not can_manage_community_list(request.user, clist):\n            permission_denied(request, \"You do not have permission to access this view\")\n\n        if clist.pk is None:\n            clist.save()\n\n        if not doc in clist.added_docs.all():\n            clist.added_docs.add(doc)\n\n        if request.is_ajax():\n            return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')\n        else:\n            return HttpResponseRedirect(clist.get_absolute_url())\n\n    return render(request, \"community/track_document.html\", {\n        \"name\": doc.name,\n    })\n\n@login_required\ndef untrack_document(request, name, username=None, acronym=None):\n    doc = get_object_or_404(Document, docalias__name=name)\n    clist = lookup_community_list(username, acronym)\n    if not can_manage_community_list(request.user, clist):\n        permission_denied(request, \"You do not have permission to access this view\")\n\n    if request.method == \"POST\":\n        if clist.pk is not None:\n            clist.added_docs.remove(doc)\n\n        if request.is_ajax():\n            return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')\n        else:\n            return HttpResponseRedirect(clist.get_absolute_url())\n\n    return render(request, \"community/untrack_document.html\", {\n        \"name\": doc.name,\n    })\n\n\ndef export_to_csv(request, username=None, acronym=None, group_type=None):\n    clist = lookup_community_list(username, acronym)\n\n    response = HttpResponse(content_type='text/csv')\n\n    if clist.group:\n        filename = \"%s-draft-list.csv\" % clist.group.acronym\n    else:\n        filename = \"draft-list.csv\"\n\n    response['Content-Disposition'] = 'attachment; filename=%s' % filename\n\n    writer = csv.writer(response, dialect=csv.excel, delimiter=str(','))\n\n    header = [\n        \"Name\",\n        \"Title\",\n        \"Date of latest revision\",\n        \"Status in the IETF process\",\n        \"Associated group\",\n        \"Associated AD\",\n        \"Date of latest change\",\n    ]\n    writer.writerow(header)\n\n    docs = docs_tracked_by_community_list(clist).select_related('type', 'group', 'ad')\n    for doc in docs.prefetch_related(\"states\", \"tags\"):\n        row = []\n        row.append(doc.name)\n        row.append(doc.title)\n        e = doc.latest_event(type='new_revision')\n        row.append(e.time.strftime(\"%Y-%m-%d\") if e else \"\")\n        row.append(strip_tags(doc.friendly_state()))\n        row.append(doc.group.acronym if doc.group else \"\")\n        row.append(str(doc.ad) if doc.ad else \"\")\n        e = doc.latest_event()\n        row.append(e.time.strftime(\"%Y-%m-%d\") if e else \"\")\n        writer.writerow(row)\n\n    return response\n\ndef feed(request, username=None, acronym=None, group_type=None):\n    clist = lookup_community_list(username, acronym)\n\n    significant = request.GET.get('significant', '') == '1'\n\n    documents = docs_tracked_by_community_list(clist).values_list('pk', flat=True)\n    since = datetime.datetime.now() - datetime.timedelta(days=14)\n\n    events = DocEvent.objects.filter(\n        doc__id__in=documents,\n        time__gte=since,\n    ).distinct().order_by('-time', '-id').select_related(\"doc\")\n\n    if significant:\n        events = events.filter(type=\"changed_state\", statedocevent__state__in=list(states_of_significant_change()))\n\n    host = request.get_host()\n    feed_url = 'https://%s%s' % (host, request.get_full_path())\n    feed_id = uuid.uuid5(uuid.NAMESPACE_URL, str(feed_url))\n    title = '%s RSS Feed' % clist.long_name()\n    if significant:\n        subtitle = 'Significant document changes'\n    else:\n        subtitle = 'Document changes'\n\n    return render(request, 'community/atom.xml', {\n        'clist': clist,\n        'entries': events[:50],\n        'title': title,\n        'subtitle': subtitle,\n        'id': feed_id.urn,\n        'updated': datetime.datetime.now(),\n    }, content_type='text/xml')\n\n\n@login_required\ndef subscription(request, username=None, acronym=None, group_type=None):\n    clist = lookup_community_list(username, acronym)\n    if clist.pk is None:\n        raise Http404\n\n    existing_subscriptions = EmailSubscription.objects.filter(community_list=clist, email__person__user=request.user)\n\n    if request.method == 'POST':\n        action = request.POST.get(\"action\")\n        if action == \"subscribe\":\n            form = SubscriptionForm(request.user, clist, request.POST)\n            if form.is_valid():\n                subscription = form.save(commit=False)\n                subscription.community_list = clist\n                subscription.save()\n\n                return HttpResponseRedirect(\"\")\n\n        elif action == \"unsubscribe\":\n            existing_subscriptions.filter(pk=request.POST.get(\"subscription_id\")).delete()\n\n            return HttpResponseRedirect(\"\")\n    else:\n        form = SubscriptionForm(request.user, clist)\n\n    return render(request, 'community/subscription.html', {\n        'clist': clist,\n        'form': form,\n        'existing_subscriptions': existing_subscriptions,\n    })\n", "path": "ietf/community/views.py"}]}