document stages on import,
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 21 Jun 2011 14:11:10 +0000 (16:11 +0200)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 21 Jun 2011 14:11:10 +0000 (16:11 +0200)
assigning documents to users,
import assignments from redmine

21 files changed:
apps/dvcs/fixtures/stages.json [new file with mode: 0644]
apps/dvcs/migrations/0001_initial.py
apps/dvcs/models.py
apps/wiki/forms.py
apps/wiki/helpers.py
apps/wiki/locale/pl/LC_MESSAGES/django.mo
apps/wiki/locale/pl/LC_MESSAGES/django.po
apps/wiki/management/__init__.py [new file with mode: 0755]
apps/wiki/management/commands/__init__.py [new file with mode: 0755]
apps/wiki/management/commands/assign_from_redmine.py [new file with mode: 0755]
apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py
apps/wiki/templates/wiki/base.html
apps/wiki/templates/wiki/chunk_list_item.html [deleted file]
apps/wiki/templates/wiki/document_list.html
apps/wiki/templates/wiki/document_list_item.html [deleted file]
apps/wiki/templates/wiki/tabs/history_view.html
apps/wiki/templates/wiki/user_list.html [new file with mode: 0755]
apps/wiki/templatetags/wiki.py
apps/wiki/urls.py
apps/wiki/views.py
redakcja/static/css/filelist.css

diff --git a/apps/dvcs/fixtures/stages.json b/apps/dvcs/fixtures/stages.json
new file mode 100644 (file)
index 0000000..fc05d57
--- /dev/null
@@ -0,0 +1,83 @@
+[
+    {
+        "pk": 1, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 1, 
+            "name": "Autokorekta", 
+            "slug": "first_correction"
+        }
+    }, 
+    {
+        "pk": 2, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 2, 
+            "name": "Tagowanie", 
+            "slug": "tagging"
+        }
+    }, 
+    {
+        "pk": 3, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 3, 
+            "name": "Korekta", 
+            "slug": "proofreading"
+        }
+    }, 
+    {
+        "pk": 4, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 4, 
+            "name": "Sprawdzenie przypis\u00f3w \u017ar\u00f3d\u0142a", 
+            "slug": "annotation-proofreading"
+        }
+    }, 
+    {
+        "pk": 5, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 5, 
+            "name": "Uwsp\u00f3\u0142cze\u015bnienie", 
+            "slug": "modernisation"
+        }
+    }, 
+    {
+        "pk": 6, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 6, 
+            "name": "Przypisy", 
+            "slug": "annotations"
+        }
+    }, 
+    {
+        "pk": 7, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 7, 
+            "name": "Motywy", 
+            "slug": "themes"
+        }
+    }, 
+    {
+        "pk": 8, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 8, 
+            "name": "Ostateczna redakcja literacka", 
+            "slug": "editor-proofreading"
+        }
+    }, 
+    {
+        "pk": 9, 
+        "model": "dvcs.tag", 
+        "fields": {
+            "ordering": 9, 
+            "name": "Ostateczna redakcja techniczna", 
+            "slug": "technical-editor-proofreading"
+        }
+    }
+]
index d9f0984..6bff095 100644 (file)
@@ -4,6 +4,7 @@ from south.db import db
 from south.v2 import SchemaMigration
 from django.db import models
 
+
 class Migration(SchemaMigration):
 
     def forwards(self, orm):
@@ -52,6 +53,10 @@ class Migration(SchemaMigration):
         ))
         db.send_create_signal('dvcs', ['Document'])
 
+        if not db.dry_run:
+            from django.core.management import call_command
+            call_command("loaddata", "stages.json")
+
 
     def backwards(self, orm):
         
index a72556f..f81f2ca 100644 (file)
@@ -39,7 +39,14 @@ class Tag(models.Model):
         sender._object_cache = {}
 
     def next(self):
-        Tag.objects.filter(ordering__gt=self.ordering)
+        """
+            Returns the next tag - stage to work on.
+            Returns None for the last stage.
+        """
+        try:
+            return Tag.objects.filter(ordering__gt=self.ordering)[0]
+        except IndexError:
+            return None
 
 models.signals.pre_save.connect(Tag.listener_changed, sender=Tag)
 
@@ -220,6 +227,9 @@ class Document(models.Model):
         author = kwargs.get('author', None)
         author_desc = kwargs.get('author_desc', None)
         tags = kwargs.get('tags', [])
+        if tags:
+            # set stage to next tag after the commited one
+            self.stage = max(tags, key=lambda t: t.ordering).next()
 
         old_head = self.head
         if parent != old_head:
index 3626b6d..e153f7e 100644 (file)
@@ -3,12 +3,14 @@
 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+from django.contrib.auth.models import User
+from django.db.models import Count
 from django import forms
-from wiki.models import Book, Chunk
 from django.utils.translation import ugettext_lazy as _
 
 from dvcs.models import Tag
 from wiki.constants import MASTERS
+from wiki.models import Book, Chunk
 
 class DocumentTagForm(forms.Form):
     """
@@ -154,6 +156,10 @@ class ChunkForm(forms.ModelForm):
     """
         Form used for editing a chunk.
     """
+    user = forms.ModelChoiceField(queryset=
+        User.objects.annotate(count=Count('document')).
+        order_by('-count', 'last_name', 'first_name'))
+
 
     class Meta:
         model = Chunk
index ee26c5a..fe4b3b8 100644 (file)
@@ -143,3 +143,28 @@ def active_tab(tab):
         return wrapped
     return wrapper
 
+
+class BookChunks(object):
+    """
+        Yields the chunks of a book.
+    """
+
+    def __init__(self, book):
+        self.book = book
+
+    @property
+    def chunks(self):
+        return self.book.chunk_set.all()
+
+
+class ChoiceChunks(BookChunks):
+    """
+        Associates the given chunks iterable for a book.
+    """
+
+    chunks = None
+
+    def __init__(self, book, chunks):
+        self.book = book
+        self.chunks = chunks
+
index 1f5e514..f841945 100644 (file)
Binary files a/apps/wiki/locale/pl/LC_MESSAGES/django.mo and b/apps/wiki/locale/pl/LC_MESSAGES/django.mo differ
index 0b102e9..c760f3a 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Platforma Redakcyjna\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-11-30 15:00+0100\n"
-"PO-Revision-Date: 2010-11-30 15:00+0100\n"
+"POT-Creation-Date: 2011-06-21 13:41+0200\n"
+"PO-Revision-Date: 2011-06-21 13:46+0100\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
 "Language: \n"
@@ -16,121 +16,223 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: constants.py:6
-msgid "First correction"
-msgstr "Autokorekta"
-
-#: constants.py:7
-msgid "Tagging"
-msgstr "Tagowanie"
-
-#: constants.py:8
-msgid "Initial Proofreading"
-msgstr "Korekta"
-
-#: constants.py:9
-msgid "Annotation Proofreading"
-msgstr "Sprawdzenie przypisów źródła"
-
-#: constants.py:10
-msgid "Modernisation"
-msgstr "Uwspółcześnienie"
-
-#: constants.py:11
-#: templates/wiki/tabs/annotations_view_item.html:3
-msgid "Annotations"
-msgstr "Przypisy"
-
-#: constants.py:12
-msgid "Themes"
-msgstr "Motywy"
-
-#: constants.py:13
-msgid "Editor's Proofreading"
-msgstr "Ostateczna redakcja literacka"
-
-#: constants.py:14
-msgid "Technical Editor's Proofreading"
-msgstr "Ostateczna redakcja techniczna"
-
-#: constants.py:18
-msgid "Ready to publish"
+#: forms.py:32
+msgid "Publishable"
 msgstr "Gotowe do publikacji"
 
-#: forms.py:49
+#: forms.py:68
 msgid "ZIP file"
 msgstr "Plik ZIP"
 
-#: forms.py:82
+#: forms.py:99
+#: forms.py:137
 msgid "Author"
 msgstr "Autor"
 
-#: forms.py:83
+#: forms.py:100
+#: forms.py:138
 msgid "Your name"
 msgstr "Imię i nazwisko"
 
-#: forms.py:88
+#: forms.py:105
+#: forms.py:143
 msgid "Author's email"
 msgstr "E-mail autora"
 
-#: forms.py:89
+#: forms.py:106
+#: forms.py:144
 msgid "Your email address, so we can show a gravatar :)"
 msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
 
-#: forms.py:95
+#: forms.py:112
+#: forms.py:150
 msgid "Your comments"
 msgstr "Twój komentarz"
 
-#: forms.py:96
+#: forms.py:113
 msgid "Describe changes you made."
 msgstr "Opisz swoje zmiany"
 
-#: forms.py:102
+#: forms.py:119
 msgid "Completed"
 msgstr "Ukończono"
 
-#: forms.py:103
+#: forms.py:120
 msgid "If you completed a life cycle stage, select it."
 msgstr "Jeśli został ukończony etap prac, wskaż go."
 
-#: models.py:94
-#, python-format
-msgid "Finished stage: %s"
-msgstr "Ukończony etap: %s"
+#: forms.py:151
+msgid "Describe the reason for reverting."
+msgstr "Opisz powód przywrócenia."
+
+#: forms.py:176
+#: forms.py:190
+msgid "Chunk with this slug already exists"
+msgstr "Część z tym slugiem już istnieje"
+
+#: forms.py:202
+msgid "Append to"
+msgstr "Dołącz do"
+
+#: models.py:25
+msgid "title"
+msgstr "tytuł"
+
+#: models.py:26
+msgid "slug"
+msgstr ""
+
+#: models.py:27
+msgid "scan gallery name"
+msgstr "nazwa galerii skanów"
+
+#: models.py:29
+msgid "parent"
+msgstr "rodzic"
+
+#: models.py:30
+msgid "parent number"
+msgstr "numeracja rodzica"
+
+#: models.py:40
+msgid "book"
+msgstr "książka"
+
+#: models.py:41
+msgid "books"
+msgstr "książki"
 
-#: models.py:153
+#: models.py:206
 msgid "name"
 msgstr "nazwa"
 
-#: models.py:157
+#: models.py:210
 msgid "theme"
 msgstr "motyw"
 
-#: models.py:158
+#: models.py:211
 msgid "themes"
 msgstr "motywy"
 
-#: views.py:167
+#: views.py:241
 #, python-format
-msgid "Title already used for %s"
-msgstr "Nazwa taka sama jak dla pliku %s"
+msgid "Slug already used for %s"
+msgstr "Slug taki sam jak dla pliku %s"
 
-#: views.py:169
-msgid "Title already used in repository."
-msgstr "Plik o tej nazwie już istnieje w repozytorium."
+#: views.py:243
+msgid "Slug already used in repository."
+msgstr "Dokument o tym slugu już istnieje w repozytorium."
 
-#: views.py:175
+#: views.py:249
 msgid "File should be UTF-8 encoded."
 msgstr "Plik powinien mieć kodowanie UTF-8."
 
-#: views.py:358
+#: views.py:655
 msgid "Tag added"
 msgstr "Dodano tag"
 
-#: templates/wiki/base.html:15
+#: views.py:677
+msgid "Revision marked"
+msgstr "Wersja oznaczona"
+
+#: views.py:679
+msgid "Nothing changed"
+msgstr "Nic nie uległo zmianie"
+
+#: templates/wiki/base.html:9
 msgid "Platforma Redakcyjna"
 msgstr ""
 
+#: templates/wiki/book_append_to.html:8
+msgid "Append book"
+msgstr "Dołącz książkę"
+
+#: templates/wiki/book_detail.html:6
+#: templates/wiki/book_detail.html.py:46
+msgid "edit"
+msgstr "edytuj"
+
+#: templates/wiki/book_detail.html:16
+msgid "add basic document structure"
+msgstr "dodaj podstawową strukturę dokumentu"
+
+#: templates/wiki/book_detail.html:20
+msgid "change master tag to"
+msgstr "zmień tak master na"
+
+#: templates/wiki/book_detail.html:24
+msgid "add begin trimming tag"
+msgstr "dodaj początkowy ogranicznik"
+
+#: templates/wiki/book_detail.html:28
+msgid "add end trimming tag"
+msgstr "dodaj końcowy ogranicznik"
+
+#: templates/wiki/book_detail.html:34
+msgid "unstructured text"
+msgstr "tekst bez struktury"
+
+#: templates/wiki/book_detail.html:38
+msgid "unknown XML"
+msgstr "nieznany XML"
+
+#: templates/wiki/book_detail.html:42
+msgid "broken document"
+msgstr "uszkodzony dokument"
+
+#: templates/wiki/book_detail.html:60
+msgid "Apply fixes"
+msgstr "Wykonaj zmiany"
+
+#: templates/wiki/book_detail.html:66
+msgid "Append to other book"
+msgstr "Dołącz do innej książki"
+
+#: templates/wiki/book_detail.html:68
+msgid "Last published"
+msgstr "Ostatnio opublikowano"
+
+#: templates/wiki/book_detail.html:72
+msgid "Full XML"
+msgstr "Pełny XML"
+
+#: templates/wiki/book_detail.html:73
+msgid "HTML version"
+msgstr "Wersja HTML"
+
+#: templates/wiki/book_detail.html:74
+msgid "TXT version"
+msgstr "Wersja TXT"
+
+#: templates/wiki/book_detail.html:76
+msgid "EPUB version"
+msgstr "Wersja EPUB"
+
+#: templates/wiki/book_detail.html:77
+msgid "PDF version"
+msgstr "Wersja PDF"
+
+#: templates/wiki/book_detail.html:90
+#: templates/wiki/tabs/summary_view.html:30
+msgid "Publish"
+msgstr "Opublikuj"
+
+#: templates/wiki/book_detail.html:94
+msgid "This book cannot be published yet"
+msgstr "Ta książka nie może jeszcze zostać opublikowana"
+
+#: templates/wiki/book_edit.html:8
+#: templates/wiki/chunk_edit.html:8
+#: templates/wiki/document_details_base.html:35
+#: templates/wiki/pubmark_dialog.html:15
+#: templates/wiki/tag_dialog.html:15
+msgid "Save"
+msgstr "Zapisz"
+
+#: templates/wiki/chunk_add.html:8
+msgid "Add chunk"
+msgstr "Dodaj część"
+
 #: templates/wiki/diff_table.html:5
 msgid "Old version"
 msgstr "Stara wersja"
@@ -147,87 +249,102 @@ msgstr "Utwórz dokument"
 msgid "Click to open/close gallery"
 msgstr "Kliknij, aby (ro)zwinąć galerię"
 
-#: templates/wiki/document_details_base.html:36
+#: templates/wiki/document_details_base.html:31
 msgid "Help"
 msgstr "Pomoc"
 
-#: templates/wiki/document_details_base.html:38
+#: templates/wiki/document_details_base.html:33
 msgid "Version"
 msgstr "Wersja"
 
-#: templates/wiki/document_details_base.html:38
+#: templates/wiki/document_details_base.html:33
 msgid "Unknown"
 msgstr "nieznana"
 
-#: templates/wiki/document_details_base.html:40
-#: templates/wiki/tag_dialog.html:15
-msgid "Save"
-msgstr "Zapisz"
-
-#: templates/wiki/document_details_base.html:41
+#: templates/wiki/document_details_base.html:36
 msgid "Save attempt in progress"
 msgstr "Trwa zapisywanie"
 
-#: templates/wiki/document_details_base.html:42
+#: templates/wiki/document_details_base.html:37
 msgid "There is a newer version of this document!"
 msgstr "Istnieje nowsza wersja tego dokumentu!"
 
-#: templates/wiki/document_list.html:30
+#: templates/wiki/document_list.html:31
 msgid "Clear filter"
 msgstr "Wyczyść filtr"
 
-#: templates/wiki/document_list.html:48
+#: templates/wiki/document_list.html:46
+msgid "No books found."
+msgstr "Nie znaleziono książek."
+
+#: templates/wiki/document_list.html:89
 msgid "Your last edited documents"
 msgstr "Twoje ostatnie edycje"
 
-#: templates/wiki/document_upload.html:9
+#: templates/wiki/document_upload.html:8
 msgid "Bulk documents upload"
 msgstr "Hurtowe dodawanie dokumentów"
 
-#: templates/wiki/document_upload.html:12
+#: templates/wiki/document_upload.html:11
 msgid "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with <code>.xml</code> will be ignored."
 msgstr "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie kończące się na <code>.xml</code> zostaną zignorowane."
 
-#: templates/wiki/document_upload.html:17
+#: templates/wiki/document_upload.html:16
+#: templatetags/wiki.py:36
 msgid "Upload"
-msgstr "Dodaj"
+msgstr "Załaduj"
 
-#: templates/wiki/document_upload.html:24
+#: templates/wiki/document_upload.html:23
 msgid "There have been some errors. No files have been added to the repository."
 msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
 
-#: templates/wiki/document_upload.html:25
+#: templates/wiki/document_upload.html:24
 msgid "Offending files"
 msgstr "Błędne pliki"
 
-#: templates/wiki/document_upload.html:33
+#: templates/wiki/document_upload.html:32
 msgid "Correct files"
 msgstr "Poprawne pliki"
 
-#: templates/wiki/document_upload.html:44
+#: templates/wiki/document_upload.html:43
 msgid "Files have been successfully uploaded to the repository."
 msgstr "Pliki zostały dodane do repozytorium."
 
-#: templates/wiki/document_upload.html:45
+#: templates/wiki/document_upload.html:44
 msgid "Uploaded files"
 msgstr "Dodane pliki"
 
-#: templates/wiki/document_upload.html:55
+#: templates/wiki/document_upload.html:54
 msgid "Skipped files"
 msgstr "Pominięte pliki"
 
-#: templates/wiki/document_upload.html:56
+#: templates/wiki/document_upload.html:55
 msgid "Files skipped due to no <code>.xml</code> extension"
 msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
 
+#: templates/wiki/pubmark_dialog.html:16
+#: templates/wiki/revert_dialog.html:39
 #: templates/wiki/tag_dialog.html:16
 msgid "Cancel"
 msgstr "Anuluj"
 
+#: templates/wiki/revert_dialog.html:38
+msgid "Revert"
+msgstr "Przywróć"
+
+#: templates/wiki/user_list.html:7
+#: templatetags/wiki.py:33
+msgid "Users"
+msgstr "Użytkownicy"
+
 #: templates/wiki/tabs/annotations_view.html:9
 msgid "all"
 msgstr "wszystkie"
 
+#: templates/wiki/tabs/annotations_view_item.html:3
+msgid "Annotations"
+msgstr "Przypisy"
+
 #: templates/wiki/tabs/gallery_view.html:7
 msgid "Previous"
 msgstr "Poprzednie"
@@ -253,8 +370,8 @@ msgid "Compare versions"
 msgstr "Porównaj wersje"
 
 #: templates/wiki/tabs/history_view.html:7
-msgid "Mark version"
-msgstr "Oznacz wersję"
+msgid "Mark for publishing"
+msgstr "Oznacz do publikacji"
 
 #: templates/wiki/tabs/history_view.html:9
 msgid "Revert document"
@@ -301,31 +418,27 @@ msgstr "Znajdź i zamień"
 msgid "Source code"
 msgstr "Kod źródłowy"
 
-#: templates/wiki/tabs/summary_view.html:10
+#: templates/wiki/tabs/summary_view.html:9
 msgid "Title"
 msgstr "Tytuł"
 
-#: templates/wiki/tabs/summary_view.html:15
+#: templates/wiki/tabs/summary_view.html:14
 msgid "Document ID"
 msgstr "ID dokumentu"
 
-#: templates/wiki/tabs/summary_view.html:19
+#: templates/wiki/tabs/summary_view.html:18
 msgid "Current version"
 msgstr "Aktualna wersja"
 
-#: templates/wiki/tabs/summary_view.html:22
+#: templates/wiki/tabs/summary_view.html:21
 msgid "Last edited by"
 msgstr "Ostatnio edytowane przez"
 
-#: templates/wiki/tabs/summary_view.html:26
+#: templates/wiki/tabs/summary_view.html:25
 msgid "Link to gallery"
 msgstr "Link do galerii"
 
-#: templates/wiki/tabs/summary_view.html:31
-msgid "Publish"
-msgstr "Opublikuj"
-
-#: templates/wiki/tabs/summary_view_item.html:4
+#: templates/wiki/tabs/summary_view_item.html:3
 msgid "Summary"
 msgstr "Podsumowanie"
 
@@ -341,8 +454,55 @@ msgstr "Wstaw przypis"
 msgid "Visual editor"
 msgstr "Edytor wizualny"
 
+#: templatetags/wiki.py:30
+msgid "Assigned to me"
+msgstr "Przypisane do mnie"
+
+#: templatetags/wiki.py:32
+msgid "Unassigned"
+msgstr "Nie przypisane"
+
+#: templatetags/wiki.py:34
+msgid "All"
+msgstr "Wszystkie"
+
+#: templatetags/wiki.py:35
+msgid "Add"
+msgstr "Dodaj"
+
+#: templatetags/wiki.py:39
+msgid "Admin"
+msgstr "Administracja"
+
+#~ msgid "First correction"
+#~ msgstr "Autokorekta"
+
+#~ msgid "Tagging"
+#~ msgstr "Tagowanie"
+
+#~ msgid "Initial Proofreading"
+#~ msgstr "Korekta"
+
+#~ msgid "Annotation Proofreading"
+#~ msgstr "Sprawdzenie przypisów źródła"
+
+#~ msgid "Modernisation"
+#~ msgstr "Uwspółcześnienie"
+
+#~ msgid "Themes"
+#~ msgstr "Motywy"
+
+#~ msgid "Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja literacka"
+
+#~ msgid "Technical Editor's Proofreading"
+#~ msgstr "Ostateczna redakcja techniczna"
+
+#~ msgid "Finished stage: %s"
+#~ msgstr "Ukończony etap: %s"
+
 #~ msgid "Refresh"
 #~ msgstr "Odśwież"
+
 #~ msgid "Insert special character"
 #~ msgstr "Wstaw znak specjalny"
-
diff --git a/apps/wiki/management/__init__.py b/apps/wiki/management/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/apps/wiki/management/commands/__init__.py b/apps/wiki/management/commands/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/apps/wiki/management/commands/assign_from_redmine.py b/apps/wiki/management/commands/assign_from_redmine.py
new file mode 100755 (executable)
index 0000000..4517749
--- /dev/null
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+
+import csv
+from optparse import make_option
+import re
+import sys
+import urllib
+import urllib2
+
+from django.contrib.auth.models import User
+from django.core.management.base import BaseCommand
+from django.core.management.color import color_style
+from django.db import transaction
+
+from slughifi import slughifi
+from wiki.models import Chunk
+
+
+REDMINE_CSV = 'http://redmine.nowoczesnapolska.org.pl/projects/wl-publikacje/issues.csv'
+REDAKCJA_URL = 'http://redakcja.wolnelektury.pl/documents/'
+
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        make_option('-r', '--redakcja', dest='redakcja', metavar='URL',
+            help='Base URL of Redakcja documents',
+            default=REDAKCJA_URL),
+        make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
+            help='Less output'),
+        make_option('-f', '--force', action='store_true', dest='force', default=False,
+            help='Force assignment overwrite'),
+    )
+    help = 'Imports ticket assignments from Redmine.'
+    args = '[redmine-csv-url]'
+
+    def handle(self, *redmine_csv, **options):
+
+        self.style = color_style()
+
+        redakcja = options.get('redakcja')
+        verbose = options.get('verbose')
+        force = options.get('force')
+
+        if not redmine_csv:
+            if verbose:
+                print "Using default Redmine CSV URL:", REDMINE_CSV
+            redmine_csv = REDMINE_CSV
+
+        # Start transaction management.
+        transaction.commit_unless_managed()
+        transaction.enter_transaction_management()
+        transaction.managed(True)
+
+        redakcja_link = re.compile(re.escape(redakcja) + r'([-_.:?&%/a-zA-Z0-9]*)')
+
+        all_tickets = 0
+        all_chunks = 0
+        done_tickets = 0
+        done_chunks = 0
+        empty_users = 0
+        unknown_users = 0
+        unknown_books = 0
+        forced = 0
+
+        if verbose:
+            print 'Downloading CSV file'
+        for r in csv.reader(urllib2.urlopen(redmine_csv)):
+            if r[0] == '#':
+                continue
+            all_tickets += 1
+
+            username = r[6]
+            if not username:
+                if verbose:
+                    print "Empty user, skipping"
+                empty_users += 1
+                continue
+
+            first_name, last_name = unicode(username, 'utf-8').rsplit(u' ', 1)
+            try:
+                user = User.objects.get(first_name=first_name, last_name=last_name)
+            except User.DoesNotExist:
+                print self.style.ERROR('Unknown user: ' + username)
+                print "'%s' '%s'" % (first_name, last_name)
+                print type(last_name)
+                unknown_users += 1
+                continue
+
+            ticket_done = False
+            for fname in redakcja_link.findall(r[-1]):
+                fname = unicode(urllib.unquote(fname), 'utf-8', 'ignore')
+                if fname.endswith('.xml'):
+                    fname = fname[:-4]
+                fname = fname.replace(' ', '_')
+                fname = slughifi(fname)
+
+                chunks = Chunk.objects.filter(book__slug=fname)
+                if not chunks:
+                    print self.style.ERROR('Unknown book: ' + fname)
+                    unknown_books += 1
+                    continue
+                all_chunks += chunks.count()
+
+                for chunk in chunks:
+                    if chunk.user:
+                        if chunk.user == user:
+                            continue
+                        else:
+                            forced += 1
+                            if force:
+                                print self.style.WARNING(
+                                    '%s assigned to %s, forcing change to %s.' %
+                                    (chunk.pretty_name(), chunk.user, user))
+                            else:
+                                print self.style.WARNING(
+                                    '%s assigned to %s not to %s, skipping.' %
+                                    (chunk.pretty_name(), chunk.user, user))
+                                continue
+                    chunk.user = user
+                    chunk.save()
+                    ticket_done = True
+                    done_chunks += 1
+
+            if ticket_done:
+                done_tickets += 1
+
+
+        # Print results
+        print
+        print "Results:"
+        print "Done %d/%d tickets, assigned %d/%d book chunks." % (
+                done_tickets, all_tickets, done_chunks, all_chunks)
+        print "%d tickets unassigned, for %d chunks assignment differed." % (
+                empty_users, forced)
+        print "Unrecognized: %d books, %d users." % (
+                unknown_books, unknown_users)
+        print
+
+
+        transaction.commit()
+        transaction.leave_transaction_management()
+
index 010ed2a..3db141a 100644 (file)
@@ -14,6 +14,7 @@ from south.v2 import SchemaMigration
 from slughifi import slughifi
 
 META_REGEX = re.compile(r'\s*<!--\s(.*?)-->', re.DOTALL | re.MULTILINE)
+STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE)
 
 
 def urlunquote(url):
@@ -93,27 +94,48 @@ def migrate_file_from_hg(orm, fname, entry):
         description=''
         )
     chunk.head = head
+    try:
+        chunk.stage = orm['dvcs.Tag'].objects.order_by('ordering')[0]
+    except IndexError:
+        chunk.stage = None
     old_data = ''
 
     maxrev = entry.filerev()
-    gallery_link = None;
+    gallery_link = None
+    
     for rev in xrange(maxrev + 1):
-        # TODO: tags
         fctx = entry.filectx(rev)
         data = fctx.data()
         gallery_link = gallery(fname, data)
         data = plain_text(data)
+
+        # get tags from description
+        description = fctx.description().decode("utf-8", 'replace')
+        tags = STAGE_TAGS_RE.findall(description)
+        tags = [orm['dvcs.Tag'].objects.get(slug=slug.strip()) for slug in tags]
+
+        if tags:
+            max_ordering = max(tags, key=lambda x: x.ordering).ordering
+            try:
+                chunk.stage = orm['dvcs.Tag'].objects.filter(ordering__gt=max_ordering).order_by('ordering')[0]
+            except IndexError:
+                chunk.stage = None
+
+        description = STAGE_TAGS_RE.sub('', description)
+
         head = orm['dvcs.Change'].objects.create(
             tree=chunk,
             revision=rev + 1,
             patch=make_patch(old_data, data),
             created_at=datetime.datetime.fromtimestamp(fctx.date()[0]),
-            description=fctx.description().decode("utf-8", 'replace'),
+            description=description,
             author_desc=fctx.user().decode("utf-8", 'replace'),
             parent=chunk.head
             )
+        head.tags = tags
         chunk.head = head
         old_data = data
+
     chunk.save()
     if gallery_link:
         book.gallery = gallery_link
@@ -246,7 +268,9 @@ class Migration(SchemaMigration):
             'Meta': {'object_name': 'Document'},
             'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
             'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dvcs.Change']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Tag']", 'null': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
         },
         'dvcs.tag': {
             'Meta': {'ordering': "['ordering']", 'object_name': 'Tag'},
index 85abaaf..5d20bbc 100644 (file)
 
     <div id="tabs-nav-left">
         {% main_tabs %}
-        <!--a {% ifequal active_tab "user" %}class="active" {% endifequal %}href="{% url wiki_user %}">{% trans "User" %}</a>
-        <a {% ifequal active_tab "all" %}class="active" {% endifequal %}href="{% url wiki_document_list %}">{% trans "All" %}</a>
-        <a {% ifequal active_tab "create" %}class="active" {% endifequal %}href="{% url wiki_create_missing %}">{% trans "Add" %}</a>
-        <a {% ifequal active_tab "upload" %}class="active" {% endifequal %}href="{% url wiki_upload %}">{% trans "Upload" %}</a-->
     </div>
 
     <span id="login-box">
diff --git a/apps/wiki/templates/wiki/chunk_list_item.html b/apps/wiki/templates/wiki/chunk_list_item.html
deleted file mode 100755 (executable)
index bec9e75..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<tr>
-    <td colspan="3">
-        <a target="_blank"
-        href="{% url wiki_chunk_edit chunk.book.slug chunk.slug %}">[?]</a>
-        <a target="_blank"
-                href="{{ chunk.get_absolute_url }}">
-                {{ chunk.pretty_name }}</a>
-    </td>
-</tr>
index 221b63d..9525078 100644 (file)
@@ -2,6 +2,7 @@
 
 {% load i18n %}
 {% load pagination_tags %}
+{% load wiki %}
 
 {% block extrabody %}
 {{ block.super }}
@@ -23,7 +24,7 @@ $(function() {
 
 {% block leftcolumn %}
        <form method="get" action="#">
-    <table id="file-list">
+    <table>
        <thead>
                <tr><th>Filtr:</th>
                        <th><input autocomplete="off" name="filter" id="file-list-filter" type="text" size="40" /></th>
@@ -31,12 +32,54 @@ $(function() {
                        </tr>
                </thead>
                <tbody>
+               </tbody>
+    </table>
+       </form>
+
+
+
+       <form method="get" action="#">
+    <table id="file-list">
+               <tbody>
         {% autopaginate books 20 %}
-       {% for book in books %}
-            {{ book.list_html }}
+        {% if not books %}
+            <tr><td>{% trans "No books found." %}</td></tr>
+        {% endif %}
+       {% for item in books %}
+            {% with item.book as book %}
+            
+            {% ifequal book.chunk_set.count 1 %}
+                <tr>
+                    <td><a target="_blank" href="{% url wiki_book book.slug %}">[B]</a></td>
+                    <td><a href="{% url wiki_chunk_edit book.slug book.0.slug %}">[c]</a></td>
+                    <td><a target="_blank"
+                                href="{% url wiki_editor book.slug %}">
+                                {{ book.title }}</a></td>
+                    <td>({{ book.0.stage }})</td>
+                    <td>{% if book.0.user %}<a href="{% url wiki_user book.0.user.username %}">{{ book.0.user.first_name }} {{ book.0.user.last_name }}</a>{% endif %}</td>
+                </tr>
+            {% else %}
+                <tr>
+                    <td><a target="_blank" href="{% url wiki_book book.slug %}">[B]</a></td>
+                    <td></td>
+                    <td>{{ book.title }}</td>
+                </tr>
+                {% for chunk in item.chunks %}
+                    <tr>
+                        <td></td>
+                        <td><a href="{% url wiki_chunk_edit book.slug chunk.slug %}">[c]</a></td>
+                        <td><a target="_blank" href="{{ chunk.get_absolute_url }}">
+                                <span class='chunkno'>{{ chunk.number }}.</span>
+                                {{ chunk.comment }}</a></td>
+                        <td>({{ chunk.stage }})</td>
+                        <td>{% if chunk.user %}<a href="{% url wiki_user chunk.user.username %}">{{ chunk.user.first_name }} {{ chunk.user.last_name }}</a>{% endif %}</td>
+                    </td></tr>
+                {% endfor %}
+            {% endifequal %}
+            {% endwith %}
        {% endfor %}
-               </tbody>
         <tr><td colspan="3">{% paginate %}</td></tr>
+               </tbody>
     </table>
        </form>
 {% endblock leftcolumn %}
diff --git a/apps/wiki/templates/wiki/document_list_item.html b/apps/wiki/templates/wiki/document_list_item.html
deleted file mode 100755 (executable)
index f1c4d37..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<tr>
-    <td colspan="3">
-        <a target="_blank" data-id="{{ book.slug }}"
-        href="{% url wiki_book book.slug %}">[?]</a>
-        {% ifequal book.chunk_set.count 1 %}
-            <a target="_blank" data-id="{{ book.slug }}"
-                    href="{% url wiki_editor book.slug %}">
-                    {{ book.title }}</a>
-        {% else %}
-            {{ book.title }}
-            <div class="chunk-list">
-            {% for chunk in book %}
-                <a target="_blank" data-id="{{ book.slug }}"
-                    href="{{ chunk.get_absolute_url }}">
-                    <span class='chunkno'>{{ forloop.counter }}.</span>
-                    {{ chunk.comment }}</a><br/>
-            {% endfor %}
-            </div>
-        {% endifequal %}
-    </td>
-</tr>
index db207bb..0764f98 100644 (file)
@@ -3,8 +3,6 @@
     <div class="toolbar">
        <button type="button" id="make-diff-button"
                        data-enabled-when="2" disabled="disabled">{% trans "Compare versions" %}</button>
-               <button type="button" id="tag-changeset-button"
-                       data-enabled-when="1" disabled="disabled">{% trans "Mark version" %}</button>
                <button type="button" id="pubmark-changeset-button"
                        data-enabled-when="1" disabled="disabled">{% trans "Mark for publishing" %}</button>
                <button type="button" id="doc-revert-button"
diff --git a/apps/wiki/templates/wiki/user_list.html b/apps/wiki/templates/wiki/user_list.html
new file mode 100755 (executable)
index 0000000..8996cc0
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends "wiki/base.html" %}
+
+{% load i18n %}
+
+{% block leftcolumn %}
+
+<h1>{% trans "Users" %}</h1>
+
+<ul>
+{% for user in users %}
+    <li><a href="{% url wiki_user user.username %}">
+        <span class="chunkno">{{ forloop.counter }}.</span>
+        {{ user.first_name }} {{ user.last_name }}</a>
+        ({{ user.count }})</li>
+{% endfor %}
+</ul>
+
+{% endblock leftcolumn %}
index 8acf761..1fb1a2e 100644 (file)
@@ -30,6 +30,7 @@ def main_tabs(context):
         tabs.append(Tab('my', _('Assigned to me'), reverse("wiki_user")))
 
     tabs.append(Tab('unassigned', _('Unassigned'), reverse("wiki_unassigned")))
+    tabs.append(Tab('users', _('Users'), reverse("wiki_users")))
     tabs.append(Tab('all', _('All'), reverse("wiki_document_list")))
     tabs.append(Tab('create', _('Add'), reverse("wiki_create_missing")))
     tabs.append(Tab('upload', _('Upload'), reverse("wiki_upload")))
index f0b602c..097de60 100644 (file)
@@ -18,6 +18,7 @@ urlpatterns = patterns('wiki.views',
     url(r'^unassigned/$', 'unassigned', name='wiki_unassigned'),
     url(r'^user/$', 'my', name='wiki_user'),
     url(r'^user/(?P<username>[^/]+)/$', 'user', name='wiki_user'),
+    url(r'^users/$', 'users', name='wiki_users'),
 
     url(r'^edit/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
         'editor', name="wiki_editor"),
index 0821488..7bff1a0 100644 (file)
@@ -9,11 +9,13 @@ from django.conf import settings
 
 from django.contrib.auth.models import User
 from django.contrib.auth.decorators import login_required
+from django.db.models import Count
 from django.views.generic.simple import direct_to_template
 from django.views.decorators.http import require_POST, require_GET
 from django.core.urlresolvers import reverse
 from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
                 ajax_require_permission, recursive_groupby, active_tab)
+from wiki import helpers
 from django import http
 from django.shortcuts import get_object_or_404, redirect
 from django.http import Http404
@@ -46,7 +48,7 @@ MAX_LAST_DOCS = 10
 @never_cache
 def document_list(request):
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
-        'books': Book.objects.all(),
+        'books': [helpers.BookChunks(b) for b in Book.objects.all()],
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
@@ -55,8 +57,18 @@ def document_list(request):
 @active_tab('unassigned')
 @never_cache
 def unassigned(request):
+    chunks = Chunk.objects.filter(user=None).order_by('book__title', 'book', 'number')
+    books = []
+    book = None
+    for chunk in chunks:
+        if chunk.book != book:
+            book = chunk.book
+            books.append(helpers.ChoiceChunks(book, [chunk]))
+        else:
+            books[-1].chunks.append(chunk)
+
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
-        'books': Chunk.objects.filter(user=None),
+        'books': books,
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
@@ -72,14 +84,32 @@ def user(request, username=None):
     else:
         user = get_object_or_404(User, username=username)
 
+    chunks = Chunk.objects.filter(user=user).order_by('book__title', 'number')
+    books = []
+    book = None
+    for chunk in chunks:
+        if chunk.book != book:
+            book = chunk.book
+            books.append(helpers.ChoiceChunks(book, [chunk]))
+        else:
+            books[-1].chunks.append(chunk)
+
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
-        'books': Chunk.objects.filter(user=user),
+        'books': books,
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
 my = login_required(active_tab('my')(user))
 
 
+@active_tab('users')
+def users(request):
+    return direct_to_template(request, 'wiki/user_list.html', extra_context={
+        'users': User.objects.all().annotate(count=Count('document')).order_by(
+            '-count', 'last_name', 'first_name'),
+    })
+
+
 @never_cache
 def editor(request, slug, chunk=None, template_name='wiki/document_details.html'):
     try:
index bb6bea7..4c5ffb3 100644 (file)
@@ -61,7 +61,7 @@ body {
 #file-list {
        overflow: visible;
        float: left;
-       max-width: 50%;
+       /*max-width: 50%;*/
        padding-right: 2%;
        border-right: 1px dashed black;
 
@@ -116,14 +116,12 @@ a:hover {
 
 .chunkno {
     font-size: .7em;
+    padding-left: 2em;
 }
 
 td {
     vertical-align: top;
 }
-.chunk-list {
-    padding-left: 2em;
-}
 
 
 .fix {