Beiträge mit dem Tag ‘python’

Djangonauten: CSV-Export nach Excel mit Umlauten

Über das Web-Framework Django (djangoproject.com) ist alles wesentliche vielfach schon gesagt, wobei dieser Beitrag  hier ein paar wesentliche praktische Aspekte aus der Sicht eines Webworkers flüssig darstellt. Lesenswert!

Nun zum Problem und eigentlichen Thema dieses Artikels, nämlich der Export von Datenbanken bzw. Tabellen aus dem Django-Admin-Backend in ein CSV-verarbeitendes Programm, also Openoffice Calc oder eben Microsoft Excel. Auf djangosnippets.org findet sich ein vernünftiger Ansatz von einem gewissen „Dek„, nur leider funktioniert er nicht mit Umlautenhttp://www.djangosnippets.org/snippets/1697/

Ein Kollege, Anatoly Ivanov hat das Problem auch erkannt und präsentiert hier drei Codeschnipsel: Sein Ansatz benutzt folgerichtig das Unicode-fähige Template-System, aber er schreibt auf eine Datei.

Im Code am Ende des Artikels habe ich die beiden Ansätze kombiniert und hinsichtlich der drei kritischen Punkte bereinigt:

  • direktes Herunterladen und Speichern aus dem Django-Admin-Backend
  • Berücksichtung von Quotations und Textfeldern (mehrzeilig) aus der Datenbank
  • Ausgabeformat Excel-kompatibel (ISO-8859-1)

So sieht eine Ausgabe beispielhaft aus:

id;bundesland;name;statflag
1;Hamburg;Hamburg;0
Django Admin: CSV-Export mit Umlauten.

Django Admin: CSV-Export mit Umlauten.

Wem das | Zeichen als Ersatz für die Hochkomma nicht gefällt, oder der Stern (*) für ein Newline, kann dies selbst austauschen oder weiter ergänzen (replace_dc).

Sinn der .replace()-Funktion ist hier schlicht,  die CSV-Struktur nicht zu zerschießen, womit die ganze Datei ab der betreffenden Zeile unbrauchbar würde.

Hier der Code, mit Dank an Dek und Anatoly:

### models.py (App-Verzeichnis)
 
from testprojekt.actions import export_als_csv
 
class Testmodell (admin.ModelAdmin):
  actions = [export_als_csv]
 
...
 
### actions.py (in der Projekt-Root)
 
import csv, codecs
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.template import Context, Template
 
def get_csv_from_dict_list(field_list, data):
  csv_line = ";".join(['{{ row.%s|addslashes }}' % field for field in field_list])
  template = "{% for row in data %}" + csv_line + "\n{% endfor %}"
  return Template(template).render(Context({"data" : data}))
 
def export_als_csv(modeladmin, request, queryset):
  if not request.user.is_staff:
    raise PermissionDenied
 
  replace_dc = { '\n' : '* ', '\r' : '', ';' : ',', '\"' : '|', '\'' : '|'}
  opts = modeladmin.model._meta
  response = HttpResponse(mimetype='text/csv')
  response['Content-Disposition'] = 'attachment; filename=%s.csv' % unicode(opts).replace('.', '_')
  w = csv.writer(response, delimiter=';')
  field_names = [field.name for field in opts.fields]
  w.writerow(field_names)
  ax = []
  for obj in queryset:
    acc = {}
    for field in field_names:
      uf = unicode(getattr(obj, field))
      for i, j in replace_dc.iteritems():
        uf = uf.replace(i,j)
        acc[field] = uf
        ax.append(acc)
  response.write (get_csv_from_dict_list(field_names, ax).encode("iso-8859-1"))
  return response
 
export_als_csv.short_description = "Markierte Daten als Excel (CSV) speichern"
 
#

Dokumente einfach aus Sharepoint 2001 und 2003 extrahieren

Werkzeuge für die Microsoft Sharepoint Portal Server (SPS) gibt es viele: Ausgefeilte, durchdachte, professionelle usw. Für die einfache Aufgabe, einen Dokumentenbaum auszulesen, genügen Grundkenntnisse der Struktur und ein paar Zeilen Programmcode. Wir betrachten im folgenden die beiden ersten Versionen von Microsoft Sharepoint Server, also 2001 und 2003:

Exportieren mit einer einzigen Zeile Shellscript: Sharepoint Portal Server 2001

Der SPS 2001 war für die Windows 2000 – Serverumgebung zugeschnitten; schon unter Windows 2003 Server läuft er nicht mehr. Microsoft hat aber neben HTTP und DAV einen Zugang per IFS (installable file system) vorgesehen, um lesenden Zugriff auf die Dokumentenstruktur eines Arbeitsbereichs zu erhalten. Das gleiche Verfahren wird übrigens auch noch von Exchange 2000 angewandt, sodaß man durch die „Hintertür“ Zugriff auf die Dateistrukturen hat.
Mit diesem Shell-Kommando auf dem W2K-Server selbst (via cmd.exe) wird ein Laufwerksbuchstabe zugeordnet:

 
subst M: \\.\backofficestorage

Arbeitet man mit dem Windows-Explorer, müssen versteckte Ordner und Dateien angezeigt werden, sonst sieht man den Ordner WORKSPACES nicht, der die eigentlichen Dateien enthält; der Arbeitsbereich  TESTDOCS unter „Anwendungen“ ist leer (Dokumentenverzeichnis grün markiert)

explorer-sharepoint2001

Eine Zusammenfassung  zum IFS drive und dem Microsoft Web Storage System finden sich hier: http://support.microsoft.com/kb/294312/en-us/

Der Artikel betont auch noch einmal, daß diese Methode zwar zum Extrahieren von Dokumenten geeignet ist, nicht aber zum Zurückschreiben, denn man wird aller Wahrscheinlichkeit nach die darunterliegende Autoren-/Rechtestruktur kompromittieren.

Sharepoint Server 2003 alias Sharepoint 2: Dokumentenexport mit IronPython

IronPython, Microsofts Python-Implementierung in der Version 2.6 bei codeplex ( http://ironpython.codeplex.com/ ) ermöglicht, Dotnet-Assemblies auf einfachste Weise einzuhängen, z.B. mit clr.AddReference(‚Microsoft.SharePoint‘)

Hier ist der Code, um eine einzelne Dokumentenbibliothek auszulesen und auf eine lokale Platte unter Beibehaltung der timestamps zu spiegeln:

# --------------------------------------------------------------------------------------
#
# Dokumentenimport von MS SharePoint.
# Die originale Ordnerstruktur wird abgebildet; Zeitstempel der Dokumente bleibt erhalten.
# Autor: weitlandt. Klaus Stein 29.10.09
#
# Umgebung: Ironpython 2.6+ (www.ironpython.com bzw. codeplex.com)
#
 
SAVE_TO = "C:/SHP2"
SITEURL = "http://localhost"
DOCROOT = "Shared Documents"
 
# -----------------------------
 
import os,sys,stat,array,io,clr
from datetime import *
from time import mktime as mktime
 
clr.AddReference('Microsoft.SharePoint')
from Microsoft.SharePoint import *
 
def storefile(f,basedir,relpath):
  ptmp,ntmp = os.path.split(relpath)
  if not os.path.isdir(basedir + ptmp):
    os.mkdir(basedir + ptmp)
  a = f.ModifiedBy.ToString()
  d = f.OpenBinary()
  m = datetime(f.TimeLastModified)
  print f.Name+" (" + d.Length.ToString()+"), Letzte Speicherung von "+a+": "+m.ToString()
  fullpath = basedir + relpath
  b = open(fullpath,mode="w+b")
  for i in d: #
    b.write(chr(i))
  b.close()
  ts = int(mktime(m.timetuple()))
  os.utime(fullpath,(ts,ts)) # timestamp setzen
 
# main()
 
tstart = datetime.now()
if not os.path.isdir(SAVE_TO):
  os.mkdir(SAVE_TO)
 
S = SPSite(SITEURL)
W = S.OpenWeb()
 
print "Importieren von: " + W.Title + " (" + W.Url + ")"
 
R = W.GetListsOfType(SPBaseType.DocumentLibrary)[DOCROOT] # nur diesen Ordner
skip = len(DOCROOT)
cnt = 0
for i in R.Items:
  relpath = i.File.ToString()[skip:] # Das Startverzeichnis abgeschneiden
  storefile(i.File,SAVE_TO,relpath)
  cnt = cnt + 1
 
print "\nFertig. Angelegt: " + cnt.ToString() + ", Zeit: " + str(datetime.now() - tstart)
W.Dispose()
S.Dispose()
# ----

…das ist alles! Für Erweiterungen bietet sich an, die Objekteigenschaften von ‚Items‘ einmal genauer anzusehen – da ist noch etliches nützliches dabei.