Feldstudie https://www.feldstudie.net Geek in Training Sun, 10 Sep 2017 12:21:04 +0000 de-DE hourly 1 https://wordpress.org/?v=4.9.8 Metronic Admin Dashboard with Django https://www.feldstudie.net/2015/04/12/metronic-admin-dashboard-with-django/ https://www.feldstudie.net/2015/04/12/metronic-admin-dashboard-with-django/#comments Sun, 12 Apr 2015 15:58:08 +0000 https://www.feldstudie.net/?p=4020 I was searching for an admin dashboard with AngularJS support for a web application to provide stats / charts, metrics and forms in a suitable and visual way. I had a look into several dashboard...

Der Beitrag Metronic Admin Dashboard with Django erschien zuerst auf Feldstudie.

]]>
metronic-previewI was searching for an admin dashboard with AngularJS support for a web application to provide stats / charts, metrics and forms in a suitable and visual way. I had a look into several dashboard themes, which are available and decided on Metronic (affiliate link). Click here to see the demo.

With the package bought, I got the theme in plain HTML with JavaScript / jQuery and an AngularJS version. But as I use Django for my projects and no Django module was provided, I had to do some changes to get it working. For those of you wondering why I’m writing this article in English: I searched for already existing tutorials on how to get Metronic working in Django, I stumbled across many requests and questions but not on a single answer. As all of them were in English, I decided to use that language so more readers can take advantage of this post.

First of all, I assume you already set up a Django project and created a virtual environment to work in. If you didn’t, please find more information here:

Prerequisites

In your Django app directory, activate the virtual environment with 

virtualenv\Scripts\activate
 (Windows) or 
source virtualenv/bin/activate
 (Linux). Then create a new Django app with 
python manage.py startapp metronic
 which will contain all the static files and logic which we will need to get Metronic up and running. Next, make a static folder inside the metronic directory which was created while executing the last command. Inside the static folder, create another folder and name it „metronic“. This helps to avoid conflicts with other files which might have the same name.

Putting the Metronic files into the right place

When done, you have to copy a lot of files from the Metronic archive you downloaded after you purchased:

copy theme\templates\<themename>\angularjs\js\* --> static\metronic\js\
copy theme\templates\<themename>\angularjs\tpl\* --> static\metronic\tpl\
copy theme\templates\<themename>\angularjs\views\* --> static\metronic\views\

copy theme\assets\* --> static\metronic\

Please not, that some of the processes might take some time, as several thousand files have to be copied. After that, create a folder named „templates“ on the same level as the static directory and copy 

theme\templates\admin2\angularjs\index.html
 into it. I would recommend to rename to index.html file to something like metronic_index.html to avoid conflicts with other modules.

You should now have a directory structure similar to this:

+---migrations
+---static
|   \---metronic
|       +---admin
|       |   +---layout
|       |   |   +---css
|       |   |   |   \---themes
|       |   |   +---img
|       |   |   \---scripts
|       |   +---layout2 (similar to layout)
|       |   +---layout3 (similar to layout)
|       |   +---layout4 (similar to layout)
|       |   \---pages
|       |       +---css
|       |       +---img
|       |       +---media
|       |       \---scripts
|       +---frontend
|       |   +---layout
|       |   |   +---css
|       |   |   +---img
|       |   |   \---scripts
|       |   +---onepage
|       |   |   +---css
|       |   |   +---img
|       |   |   \---scripts
|       |   \---pages
|       |       +---css
|       |       +---img
|       |       \---scripts
|       +---global
|       +---js
|       |   +---controllers
|       |   \---scripts
|       +---tpl
|       \---views
|           +---datatables
|           \---profile
\---templates

Replacement of dynamic paths in Metronic code

In the next steps, we have to do some massive find/replace actions as we won’t get far with the dynamic path used in the html, javascript and css files. I used PyCharm for that which provides an excellent find/replace tool which is able to use regular expressions and executes recursively through the folder hierarchy. Notepad++ might be of help as well.

The following replacements have to be executed. If you need more explanation or help on them, please drop me a comment:

  1. replace 
    "(..\/..\/..\/assets)([\.\/\w\d-_]*)"
     with 
    "{% static 'metronic$2' %}"
     in all css, js and html files
  2. replace 
    Metronic.setAssetsPath('../../../assets/');
     with 
    Metronic.setAssetsPath('{% static 'metronic/' %}');
  3. replace 
    "'(\w+\/[\w\.-]*)'"
     with 
    "'{% static 'metronic/$1' %}'"
  4. replace 
    '../../../assets/
     with 
    '/static/metronic/
  5. replace
    'js/
     with 
    '/static/metronic/js/
  6. replace
    views/
     with 
    /static/metronic/views/
  7. replace
    'js/
     with 
    '/static/metronic/js/

As some html is not served through Django, the {% static … %} tag won’t work. Therefore, we have to replace some changes again, we just made:

  1. replace
    "\{% static 'metronic([\/\w\d\.-]*)' %}"
     with
    "/static/metronic$1"
      for static/metronic/tpl/*
  2. replace
    "\{% static 'metronic([\/\w\d\.-]*)' %}"
     with
    "/static/metronic$1"
      for static/metronic/views/*
  3. replace
    "\{% static 'metronic([\/\w\d\.-_]*)' %}"
     with
    "/static/metronic$1"
      for static/metronic/*

Making Metronic available through Django

After that, you should create a new view to have Django deliver the html file. Here is an example how the views.py could look like:

from django.views.generic import TemplateView


class MetronicAdmin(TemplateView):
    template_name = 'metronic_index.html'

Now, you just need to add an url pattern into the main urls.py:

from django.conf.urls import patterns, url
from metronic.views import MetronicAdmin

urlpatterns = patterns(
    '',
    url(r'^/', MetronicAdmin.as_view(), name='metronicadmin'),
)

After that, you should be able to run the Django runserver command and see your Metronic under http://127.0.0.1:8000

If you have any questions or remarks, please drop me a comment.

Der Beitrag Metronic Admin Dashboard with Django erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2015/04/12/metronic-admin-dashboard-with-django/feed/ 5
Ignorieren von Dateien in Git ohne Nutzung von gitignore https://www.feldstudie.net/2015/04/09/ignorieren-von-dateien-in-git-ohne-nutzung-von-gitignore/ https://www.feldstudie.net/2015/04/09/ignorieren-von-dateien-in-git-ohne-nutzung-von-gitignore/#respond Thu, 09 Apr 2015 10:29:50 +0000 https://www.feldstudie.net/?p=4013 Gerade bei öffentlichen Repositories wird mit verschiedenen Entwicklungsumgebungen, Tools und ähnlichem gearbeitet. Viele von diesen legen anwendungsspezifische Dateien an, in welchen Informationen wie Pfade, Einstellungen, etc. gespeichert werden und welche nichts in Repositories zu suchen...

Der Beitrag Ignorieren von Dateien in Git ohne Nutzung von gitignore erschien zuerst auf Feldstudie.

]]>
Gerade bei öffentlichen Repositories wird mit verschiedenen Entwicklungsumgebungen, Tools und ähnlichem gearbeitet. Viele von diesen legen anwendungsspezifische Dateien an, in welchen Informationen wie Pfade, Einstellungen, etc. gespeichert werden und welche nichts in Repositories zu suchen haben. Rein aus Gewohnheit wäre meine erste Wahl zum Ausschluss der Dateien auf die Datei .gitignore gefallen, mit Hilfe derer man Dateien von Repos ausschließen kann. Man stelle sich jedoch vor, in einem Open Source Projekt würden 100 Menschen arbeiten, welche 50 verschiedene Anwendungen nutzen, bei welchen Konfigurationen, etc. auszuschließen wären. Die .gitignore würde vmtl. unübersichtlich und die Wartung würde sich schwierig gestalten.

Eine Alternative, welche ich hier vorstellen möchte, .git/info/exclude. Hierin können Dateien oder gar Verzeichnisse vollständig _lokal_ von der Beachtung durch Git ausgeschlossen werden, ohne dass dies mit dem Repository synchronisiert wird. Wie dies funktioniert, beschreibe ich nun in den folgenden Schritten exemplarisch an dem öffentlichen Projekt „django-dashing„, an welchem ich gelegentlich mitwirke: 

Ich verwende für Python die Entwicklungsumgebung PyCharm von JetBrains. Sobald ich ein Projekt erstelle oder ein Verzeichnis als Projekt öffne, wird ein Verzeichnis „.idea“ erstellt. Dieses ist jedoch nicht gut in einem public Repository aufgehoben, da diese Dateien aufgrund der enthaltenen Pfade, Einstellungen, etc. auf anderen Rechnern andere Inhalte haben oder andere Entwickler dieses Verzeichnis nicht benötigen, da sie andere oder keine Entwicklungsumgebungen verwenden.

Beim ersten Commit nach dem Anlegen des Projekts finden sich nun folgende nicht-getrackte Dateien im Verzeichnis:

2015-04-09_11-31-03

Auf der Kommandozeile sähe dies wie folgt aus:

$ git status
# On branch pypi
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       .idea/
nothing added to commit but untracked files present (use "git add" to track)

 

Um nun das Verzeichnis und die enthaltenen Dateien lokal vom Repository auschließen, habe ich in der .git/info/exclude den Eintrag „.idea/*“ hinzufügt, so dass die exclude folgendermaßen aussieht:

# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

.idea/*

Die Syntax der Einträge ist gleich denen, die auch innerhalb der .gitignore Anwendung finden. Nach dem Speichern aktualisiere ich die Commit-Ansicht in GitExtensions und kann feststellen, dass die .idea-Einträge vollständig ignoriert werden und kein Update der .gitignore vorliegt:

2015-04-09_11-34-18

Eine Überprüfung mittels „git status“ zeigt das gleiche Ergebnis:

$ git status
# On branch pypi
nothing to commit, working directory clean

 

 

Ich hoffe, ich dieser Artikel dient nicht nur als Gedankenstütze für mich, sondern hat auch euch geholfen. 😉 Über Fragen oder Anmerkungen in den Kommentaren freue ich mich.

Der Beitrag Ignorieren von Dateien in Git ohne Nutzung von gitignore erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2015/04/09/ignorieren-von-dateien-in-git-ohne-nutzung-von-gitignore/feed/ 0
Django gehostete Webseite über iFrame einbinden https://www.feldstudie.net/2014/04/05/django-gehostete-webseite-uber-iframe-einbinden/ https://www.feldstudie.net/2014/04/05/django-gehostete-webseite-uber-iframe-einbinden/#comments Sat, 05 Apr 2014 18:11:16 +0000 http://www.feldstudie.net/?p=3925 Ich habe mir in den letzten Tagen vermehrt Django angeschaut, da ich gerne über Python Webseiten bzw. Webapplikationen  bereitstellen möchte. Nicht nur, dass ich der Meinung bin, dass Django Anfragen schneller bearbeitet und ressourcenschonender...

Der Beitrag Django gehostete Webseite über iFrame einbinden erschien zuerst auf Feldstudie.

]]>
Ich habe mir in den letzten Tagen vermehrt Django angeschaut, da ich gerne über Python Webseiten bzw. Webapplikationen  bereitstellen möchte. Nicht nur, dass ich der Meinung bin, dass Django Anfragen schneller bearbeitet und ressourcenschonender ist als Apache (inbesondere hinter Nginx), sondern auch, dass die Performance und Sicherheit PHP überlegen ist. Darüber hinaus bietet Python weitreichendere Funktionalität, wenn es z.B. um Websockets oder NoSQL-Datenbanken geht.

Auf Django selbst bin ich über das Buch „Test-Driven Web Development with Python“ von Harry Percival aufmerksam geworden. Eigentlich wollte ich mich nur in das Thema „Test-Driven Development“ einarbeiten, konnte dann aber dem Angebot von O’Reilly nicht widerstehen.

Nachdem ich mich ein wenig eingearbeitet habe und die nächsten Webseiten mit Ausrichtung auf Statistiken und Monitoring in der Planung habe, fiel mir ein, wie schön es doch wäre diese bzw. einen Teil dieser Seiten per iFrame auf andere Webseiten, wie z.B. Blog einzubinden. Damit ich es nicht vergesse und vielleicht auch jemand anderes hiervon profitiert, schreibe ich einfach einen kleinen Blogartikel hierzu:

2014-04-05_19-41-09

Nutzen wir einfach die Beispiel-Applikation aus dem oben genannten Buch. Während noch vor ein paar Monaten oder wenigen Jahren gerne Blogs als Programmiereinstiege verwendet wurden, sind es heute Todo-Listen. Warum sollte es hier also anders sein.

Wie müssen wir also vorgehen, um die Seite welche beim Aufruf von http://localhost:8000 durch Django bereitgestellt wird, auf einer anderen Seite innerhalb eines iFrames anzeigen zu lassen? Die View, welche das „home.html“ template lädt und ausliefert, sieht folgendermaßen aus:

def home_page(request):
    return render(request, 'home.html')

Da iFrames besonderen Sicherheitsanfoderungen unterliegen, müssen wir nun „from django.views.decorators.clickjacking import xframe_options_exempt“ der views.py hinzufügen und den Decorator „@xframe_options_exempt“ der „home_page“ view hinzufügen:

from django.views.decorators.clickjacking import xframe_options_exempt

@xframe_options_exempt
def home_page(request):
    return render(request, 'home.html')

Das Einbetten des iFrames gestaltet sich mit dem folgenden Code nun sehr einfach:

<body>
test vor iframe
    <iframe id="iframe" src="http://localhost:8000" style="border: none; width: 100%; height: 400px;"></iframe>
test nach iframe
</body>

Zu Berücksichtigen ist allerdings, dass in dem „Todo-List“ Beispiel beim Eingeben eines neuen Todos ein html-form gesendet wird, welches anschließend auf eine andere URL und somit eine andere view führt. Soll nun im iFrame kein Fehler beim Senden des Formulars angezeigt werden, muss der iFrame-Decorator auch den anderen Views hinzugefügt werden.

Falls Ihr Verbesserungsvorschläge oder Ergänzungen habt, schreibt mir einfach in den Kommentaren.

Der Beitrag Django gehostete Webseite über iFrame einbinden erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2014/04/05/django-gehostete-webseite-uber-iframe-einbinden/feed/ 2
Überprüfung der Performance von Funktionen in Python mit timeit https://www.feldstudie.net/2014/01/06/uberprufung-der-performance-von-funktionen-in-python-mit-timeit/ https://www.feldstudie.net/2014/01/06/uberprufung-der-performance-von-funktionen-in-python-mit-timeit/#respond Mon, 06 Jan 2014 14:18:45 +0000 http://www.feldstudie.net/?p=3911 In einem meiner letzten Blogartikel wurde ich von Alex in den Kommentaren auf eine mögliche Verbesserung hingewiesen. Und zwar ging es um den folgenden Codeschnipsel, mit welchem ich den Unix-Timestamp der vollen Stunde, bei Angabe...

Der Beitrag Überprüfung der Performance von Funktionen in Python mit timeit erschien zuerst auf Feldstudie.

]]>
In einem meiner letzten Blogartikel wurde ich von Alex in den Kommentaren auf eine mögliche Verbesserung hingewiesen.

Und zwar ging es um den folgenden Codeschnipsel, mit welchem ich den Unix-Timestamp der vollen Stunde, bei Angabe eines beliebigen Timestamps, berechnet habe:

import time
import datetime

timestamp = '1387965457'  # der Original-Timestamp
datetimetuple = datetime.datetime.fromtimestamp(int(timestamp))  # Umwandeln in ein datetime-Objekt
print datetimetuple
datetime_fullhour = datetime.datetime(  # Erstellen eines neuen datetime-Objekts, in welchem Minuten und Sekunden genullt sind
    datetimetuple.year,
    datetimetuple.month,
    datetimetuple.day,
    datetimetuple.hour)
print datetime_fullhour
timestamp_fullhour = int(time.mktime(datetime_fullhour.timetuple()))  # Umwandeln des neuen Objekts in einen Unix-Timestamp
print timestamp_fullhour

Der Code von Alex sieht hingegeben bedeutend einfacher aus:

fullhour = timestamp - (timestamp % (60*60))

Zur Überprüfung, ob beide Herangehensweisen wirklich das selbe Ergebnis liefern, habe ich den Code entsprechend angepasst:

import datetime
import time

timestamp = '1387965457'  # der Original-Timestamp

def test1():
    datetimetuple = datetime.datetime.fromtimestamp(int(timestamp))  # Umwandeln in ein datetime-Objekt
    datetime_fullhour = datetime.datetime(  # Erstellen eines neuen datetime-Objekts, in welchem Minuten und Sekunden genullt sind
        datetimetuple.year,
        datetimetuple.month,
        datetimetuple.day,
        datetimetuple.hour)
    timestamp_fullhour = int(time.mktime(datetime_fullhour.timetuple()))  # Umwandeln des neuen Objekts in einen Unix-Timestamp
    return timestamp_fullhour

def test2():
    fullhour = int(timestamp) - (int(timestamp) % (60*60))
    return fullhour

print test1()
print test2()

Wie sich herausstellte, liefern beide Funktionen das gleiche Ergebnis. In der Diskussion kam jedoch noch die Vermutung auf, dass der Code von Alex ggf. schneller sein könnte, da weniger Module und auch weniger Objekt-Instanzen benötigt werden. Um dies zu prüfen, habe ich den Quelltext umgeschrieben und das Python-Module ‚timeit‘ verwendet. Damit sah der Code wie folgt aus:

import datetime
import time
import timeit

timestamp = '1387965457'  # der Original-Timestamp

def test1():
    datetimetuple = datetime.datetime.fromtimestamp(int(timestamp))  # Umwandeln in ein datetime-Objekt
    datetime_fullhour = datetime.datetime(  # Erstellen eines neuen datetime-Objekts, in welchem Minuten und Sekunden genullt sind
        datetimetuple.year,
        datetimetuple.month,
        datetimetuple.day,
        datetimetuple.hour)
    timestamp_fullhour = int(time.mktime(datetime_fullhour.timetuple()))  # Umwandeln des neuen Objekts in einen Unix-Timestamp
    return timestamp_fullhour

def test2():
    fullhour = int(timestamp) - (int(timestamp) % (60*60))
    return fullhour

print timeit.timeit(test1, number=10000)
print timeit.timeit(test2, number=10000)

Das Ergebnis zeigte, dass mein ursprünglicher Code fast 4x langsamer war, als der von Alex. Um noch einen drauf zusetzen, habe ich den timestamp nicht als string sondern als integer deklariert und die Umwandlung von string zu integer (mittels int()) entfernt. Das Ergebnis überraschte! Durch diese, doch sehr kleine, Änderung unterschied sich die Performance beider Funktionen um Faktor 20.

Weitere Erklärungen und die Ergebnisse des Benchmarkings, könnt Ihr im folgenden Video sehen:

Über weitere Empfehlungen, gerade in Bezug auf timeit, freue ich mich!

Der Beitrag Überprüfung der Performance von Funktionen in Python mit timeit erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2014/01/06/uberprufung-der-performance-von-funktionen-in-python-mit-timeit/feed/ 0
Reload einer Konfigurationsdatei bei Änderung in Python mit watchdog Modul https://www.feldstudie.net/2014/01/05/reload-einer-konfigurationsdatei-bei-anderung-in-python-mit-watchdog-modul/ https://www.feldstudie.net/2014/01/05/reload-einer-konfigurationsdatei-bei-anderung-in-python-mit-watchdog-modul/#respond Sun, 05 Jan 2014 10:06:37 +0000 http://www.feldstudie.net/?p=3902 Um Werte in einem Python-Script einfach ändern zu können, lege ich meist im Scriptverzeichnis eine config.py ab, welche die benötigten Werte und Einstellungen enthält. Ab und an, ist es jedoch notwendig, diese Werte anzupassen....

Der Beitrag Reload einer Konfigurationsdatei bei Änderung in Python mit watchdog Modul erschien zuerst auf Feldstudie.

]]>
Um Werte in einem Python-Script einfach ändern zu können, lege ich meist im Scriptverzeichnis eine config.py ab, welche die benötigten Werte und Einstellungen enthält. Ab und an, ist es jedoch notwendig, diese Werte anzupassen. Besonders schön wäre es, wenn das Python-Script diese Änderungen übernimmt, ohne neu gestartet werden zu müssen.

Für ein aktuelles Projekt, sieht die config.py aktuell wie folgt aus:

config_parser = {
    'database': {
        'item': {
            'version': 2
        }
    }
}

In dem eigentlichen Script importiere ich die Settings wie folgt:

import config

class Parser(object):
    def __init__(self):
        self.config = config.config_parser

Um die Überwachung auf Änderungen hinzuzufügen, importiert man ‚Observer‘ und ‚FileSystemEventHandler‘ aus dem ‚watchdog‘-Modul (http://pythonhosted.org/watchdog/). Anschließend erstellt man seinen eigenen FilesystemEventhandler als Sub-Class von FileSystemEventHandler und überschreibt die __init__ und on_modified Methoden, damit man die Möglichkeit hat, eine Methode der Parser-Klasse mit der gewünschten Funktionalität aufzurufen:

class MyFileSystemEventHandler(FileSystemEventHandler):
    def __init__(self, method_to_call):
        self.__method_to_call = method_to_call

    def on_modified(self, event):
        logger.debug('calling method')
        self.__method_to_call()

Anschließend erstellt man in der __init__ Methode der Parser Class eine Instanz des eigenen FileSystemEventHandlers und übergibt die Methode, welche vom Handler bei Änderungen aufgerufen werden soll. Danach instanziert man noch den Observer, welcher die eigene Überwachung des Dateisystems durchführt und man ist eigentlich fertig.

In meinem Fall habe ich noch einen kleinen Tweak hinzugefügt. Bei meiner IDE (PyCharm) werden bei Dateiänderungen im Projektverzeichnis temporär Backup-Kopien und .pyc-Dateien angelegt. Dieses führt natürlich zu einer multiplen Ausführung der gewünschten Methode. Daher habe ich die on_modified Methode noch ein wenig ergänzt:

class MyFileSystemEventHandler(FileSystemEventHandler):
    def on_modified(self, event):
        files_to_react_on = ['config.py']
        if os.path.basename(event.src_path) in files_to_react_on:
            logger.debug('calling method')
            self.__method_to_call()
        else:
            logger.debug('just non interesting files changed')

In einer Liste gebe ich die Dateinamen an, bei deren Änderung der Aufruf durchgeführt werden kann. Hilfreich hierbei ist das Attribut ’src_path‘ des event-Objekts, welches den vollständigen Pfad der geänderten Datei beinhaltet. Über os.path.basename() lassen wir uns von diesem Pfad nur den Dateinamen ausgeben und vergleichen diesen mit unserer Liste.

Hier nochmal das vollständige Script:

import config
import os
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class MyFileSystemEventHandler(FileSystemEventHandler):
    def __init__(self, method_to_call):
        self.__method_to_call = method_to_call

    def on_modified(self, event):
        files_to_react_on = ['config.py']
        if os.path.basename(event.src_path) in files_to_react_on:
            logger.debug('calling method')
            self.__method_to_call()
        else:
            logger.debug('just non interesting files changed')

class Parser(object):
    def __init__(self):
        self.config = config.config_parser
        filewatcher_event_handler = MyFileSystemEventHandler(self.load_config)
        filewatcher_observer = Observer()
        filewatcher_observer.schedule(filewatcher_event_handler, '.', recursive=False)
        filewatcher_observer.start()

    def load_config(self):
        import config
        reload(config)
        self.config = config.config_parser
        logger.info('loaded config: %s' % (json.dumps(self.config),))

Das ganze gibt es hier noch als Video:

Freue mich auf Eure Kommentare!

Der Beitrag Reload einer Konfigurationsdatei bei Änderung in Python mit watchdog Modul erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2014/01/05/reload-einer-konfigurationsdatei-bei-anderung-in-python-mit-watchdog-modul/feed/ 0
Python – Timestamp der letzten vollen Stunde https://www.feldstudie.net/2013/12/25/python-timestamp-der-letzten-vollen-stunde/ https://www.feldstudie.net/2013/12/25/python-timestamp-der-letzten-vollen-stunde/#comments Wed, 25 Dec 2013 10:32:22 +0000 http://www.feldstudie.net/?p=3899 Ich erhalte innerhalb eines Scripts aus verschiedenen Datenquellen Informationen, in welchen ein Unix-Timestamp hinterlegt sind. Diese Daten möchte ich, jeweils für die volle Stunde, in eine Datenbank speichern. Die Aufgabe war daher, einen neuen Timestamp...

Der Beitrag Python – Timestamp der letzten vollen Stunde erschien zuerst auf Feldstudie.

]]>
Ich erhalte innerhalb eines Scripts aus verschiedenen Datenquellen Informationen, in welchen ein Unix-Timestamp hinterlegt sind. Diese Daten möchte ich, jeweils für die volle Stunde, in eine Datenbank speichern. Die Aufgabe war daher, einen neuen Timestamp zu erzeugen, in welchem die Minuten und Sekunden des originalen Timestamps nicht berücksichtigt sind. Hier das Script mit der Lösung:

import time
import datetime

timestamp = '1387965457'  # der Original-Timestamp
datetimetuple = datetime.datetime.fromtimestamp(int(timestamp))  # Umwandeln in ein datetime-Objekt
print datetimetuple
datetime_fullhour = datetime.datetime(  # Erstellen eines neuen datetime-Objekts, in welchem Minuten und Sekunden genullt sind
    datetimetuple.year,
    datetimetuple.month,
    datetimetuple.day,
    datetimetuple.hour)
print datetime_fullhour
timestamp_fullhour = int(time.mktime(datetime_fullhour.timetuple()))  # Umwandeln des neuen Objekts in einen Unix-Timestamp
print timestamp_fullhour

Und hier ist der Output:

2013-12-25 10:57:37
2013-12-25 10:00:00
1387962000

Falls Ihr Verbesserungsvorschläge oder Anmerkungen habt, einfach in die Kommentare damit 🙂

Der Beitrag Python – Timestamp der letzten vollen Stunde erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2013/12/25/python-timestamp-der-letzten-vollen-stunde/feed/ 6
Unable to find vcvarsall.bat bei Installation von Python-Paketen unter Windows https://www.feldstudie.net/2013/12/06/unable-to-find-vcvarsall-bat-bei-installation-von-python-paketen-unter-windows/ https://www.feldstudie.net/2013/12/06/unable-to-find-vcvarsall-bat-bei-installation-von-python-paketen-unter-windows/#respond Fri, 06 Dec 2013 22:03:22 +0000 http://www.feldstudie.net/?p=3890 Ich arbeite derzeit an einem Projekt in Python, und benötige hierzu Twisted für TCP- und FTP-Verbindungen zwischen den Clients und Servern. Da Twisted jedoch auch C-Code im Bauch hat, muss bei der Installation des...

Der Beitrag Unable to find vcvarsall.bat bei Installation von Python-Paketen unter Windows erschien zuerst auf Feldstudie.

]]>
Ich arbeite derzeit an einem Projekt in Python, und benötige hierzu Twisted für TCP- und FTP-Verbindungen zwischen den Clients und Servern. Da Twisted jedoch auch C-Code im Bauch hat, muss bei der Installation des Moduls, der Quellcode kompiliert werden. Im Gegensatz zu unixoiden Betriebssystemen, welche gcc als Compiler oft mit an Bord haben, ist dies bei Windows nicht der Fall.

Aus diesem Grund erscheint bei der Installation bzw. dem Buildprozess folgender Fehler (Auszug):

error: Unable to find vcvarsall.bat

----------------------------------------
Cleaning up...
Command C:\Python27\python.exe -c "import setuptools;__file__='C:\\Users\\Torsten\\AppData\\Local\\Temp\\pycharm-packaging1813359792106864279.tmp\\Twisted\\setup.py';exec(compile(open(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record c:\users\torsten\appdata\local\temp\pip-p2iin_-record\install-record.txt --single-version-externally-managed failed with error code 1 in C:\Users\Torsten\AppData\Local\Temp\pycharm-packaging1813359792106864279.tmp\Twisted
Storing complete log in C:\Users\Torsten\pip\pip.log

Nachdem ich ein wenig gesucht habe, bin ich auf den Blog von ultrainfinitum gestoßen, in welchem mögliche Lösungen beschrieben werden.

Ich habe mich für Lösung 2 entschieden und GCC für Windows installiert. Das Installationspaket fügt GCC automatisch dem Systempfad hinzu und ändert entsprechend den Standardcompiler. Den vollständigen ‚Vorgang‘ könnt Ihr Euch in meinem YouTube-Video anschauen. Viel Spaß. 🙂

Der Beitrag Unable to find vcvarsall.bat bei Installation von Python-Paketen unter Windows erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2013/12/06/unable-to-find-vcvarsall-bat-bei-installation-von-python-paketen-unter-windows/feed/ 0
Picdump 02.06.13 https://www.feldstudie.net/2013/06/02/picdump-02-06-13/ https://www.feldstudie.net/2013/06/02/picdump-02-06-13/#comments Sun, 02 Jun 2013 17:03:38 +0000 http://www.feldstudie.net/?p=3855 Lange vernachlässigt und doch begehrt, hier ist er wieder, der Picdump! 🙂 

Der Beitrag Picdump 02.06.13 erschien zuerst auf Feldstudie.

]]>
Lange vernachlässigt und doch begehrt, hier ist er wieder, der Picdump! 🙂 

Der Beitrag Picdump 02.06.13 erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2013/06/02/picdump-02-06-13/feed/ 3
Das Portfolio von Hosteurope unter die Lupe genommen https://www.feldstudie.net/2013/06/01/das-portfolio-von-hosteurope-unter-die-lupe-genommen/ https://www.feldstudie.net/2013/06/01/das-portfolio-von-hosteurope-unter-die-lupe-genommen/#comments Sat, 01 Jun 2013 10:52:14 +0000 http://www.feldstudie.net/?p=3844 Die Zeiten des Web 1.0 sind vorbei. Statisches HTML ist nicht nur seit Jahren out, es wird im besten Falle nur noch müde belächelt. Meiner Ansicht nach, sind wir derzeit in einem Wandel vom...

Der Beitrag Das Portfolio von Hosteurope unter die Lupe genommen erschien zuerst auf Feldstudie.

]]>
Die Zeiten des Web 1.0 sind vorbei. Statisches HTML ist nicht nur seit Jahren out, es wird im besten Falle nur noch müde belächelt. Meiner Ansicht nach, sind wir derzeit in einem Wandel vom Web 2.0, in welchem Webseiten dynamisch und das Netz zu einem „Mitmach-Web“ wurde, zu einem Web 3.0. Diese „next Generation“ des Internets geht weg von einer Seite, welche lediglich Informationen, wenn auch dynamisch generiert, bereitstellt. Die Zukunft sind Applikationen, die Logik sowohl client- als auch serverseitig ausführen können und welche kaum von nativen Programmen, auf Rechnern oder mobilen Geräten auf iOS- oder Android-Basis, unterschieden werden können.

Doch welche Voraussetzungen müssen erfüllt werden, um Kunden und Nutzern solche Applikationen anbieten zu können? Die üblichen Webhosting-Pakete, bei denen meist lediglich Dateien auf den Webserver gelegt werden können, bieten hier mittlerweile nicht mehr genügend Möglichkeiten, um die o.g. Funktionalität zu bieten. Anbieter wie Hosteurope bieten daher als kleinste Option vServer und dedicated bzw. root-Server an. Hier ist die Besonderheit, dass ein SSH-Zugang, mit meist administrativem Zugriff, bereitgestellt wird, der einem die Möglichkeit gibt, Anwendungen, Dienste, etc. nachzuinstallieren und anzupassen. Möchte man mehr als ein einfaches CMS wie WordPress nutzen, ist dies zwingend erforderlich. Ich habe mir daher die verschiedenen Angebote von Hosteurope mal angeschaut:

WebServer Basic

Host Europe WebServer Basic sind professionell administrierte virtuelle Server mit garantierten Ressourcen und eigener IP-Adresse, bei denen wir das Betriebssystem für Sie verwalten und überwachen. Sie bedienen Ihren virtuellen Server einfach über eine leicht zu bedienende Weboberfläche.
Ideal für Websites mit hohem Besucheraufkommen und dynamischen Inhalten. (Link)

Die WebServer Basic Pakete sind gemanagte virtuelle Server, welche über die Leistungen von den einfachen WebPack-Paketen von Hosteurope hinausgehen. Im Gegensatz zu „echten“ virtuellen Servern sind für die Konfiguration keine Administrator-Kenntnisse erforderlich um von den Vorteilen eines virtuellen Servers, z.B. Cron-Jobs, SSL-Zertifikate, SSH-Zugriff, dedizierter Arbeitsspeicher (RAM), zu profitieren. Wer also größere, dynamische Websites mit höherem Besuchaufkommen anbieten möchte und z.B. eine eigene IP-Adresse oder hohe Festplattenkapazität benötigt, jedoch keine oder nur geringe Administrations-Kenntnisse mitbringt, ist bei diesen Paketen gut aufgehoben. Aus eigener Erfahrung würde ich jedoch kein Angebot mit weniger als 2 GB RAM empfehlen, da fertige Anwendungen wie beispielsweise Wikimedia bereits hohe Anforderungen an den Arbeitsspeicher mitbringen.

WebServer Premium

Wem die WebServer Basic Pakete zu klein sind und seinen komplett eigenen Webserver haben, jedoch nicht auf das professionelle Management des Betriebssystem und die Konfiguration durch Hosteurope verzichten möchte, sollte sich die WebServer Premium Angebote genauer ansehen. Sowohl die Anzahl der CPU-Cores als auch die Größe des Arbeitsspeichers sind anspruchsvollen Web-Auftritten und -Applikationen gewachsen. So bietet das Flagschiff „WebServer Premium MAX“ 4x 600 GB SAS + 2x 100 GB SSD Festplatten, 32 GB Speicher und 2 Hexa-Core Intel Xeon CPUs, welche selbst höchsten Ansprüchen genügt.

Virtual Server

Wer lieber sein eigener Herr ist, Administrations-Aufgaben selbst durchführen möchte und auf root-Zugriff angewiesen ist, sollte sich die Virtual Server von Hosteurope anschauen. Diese bieten dem Kunden reservierte Ressourcen (RAM, virtuelle CPU, Peak-Bandbreite), verschiedene Betriebssysteme (Debian, Ubuntu, CentOS, Windows Server 2008 R2 Datacenter) und, sofern gewünscht, Parallels Plesk Panel für die webbasierte Server-Administration bei völliger Konfigurationsfreiheit. Dies ist z.B. erforderlich, wenn zusätzliche Software wie z.B. TeamSpeak, nicht-MySQL-Datenbanken, Node.js oder ähnliches installiert und genutzt werden soll. Insbesondere ist dies für jene interessant, welche mehrere Webseiten oder -Applikationen mit SSL-Verschlüsselung auf dem selben Server betreiben möchten, da pro Domain ein separates Zertifikat verwendet werden muss und hierfür jeweils eine eigene IP-Adresse benötigt wird.

Root Server

Wer seinen Server selbst konfigurieren möchte, jedoch mehr Leistung als bei einem der Virtual Server Pakete benötigt, ist mit den Root Servern von Hosteurope gut aufgehoben. Das kleinste Paket bietet bereits 4 virtuelle Cores, 24 GB Arbeitsspeicher und 1 GB HDD im Raid 1. Ab dem XXL Angebot werden sogar 12 echte Cores und 48 GB Arbeitsspeicher, ein Raid 5 und eine Peak-Bandbreite von 500 Mbit/s angeboten.

Fazit

Wie meine treuen Leser bereits mitbekommen haben, arbeite ich seit einiger Zeit mit Node.js und Couchbase Server, welche ich bald in einem Projekt verwenden werde. Da dieses parallel zu Feldstudie.net laufen und SSL-Verschlüsselung benötigen wird, wäre meine Wahl eines der Virtual Server Angebote von Hosteurope. Dort habe ich die „Freiheit“ die ich benötige, ausreichend Hardware-Ressourcen und Bandbreite. Die Preise können bei der gebotenen Leistung durchaus mit der Konkurrenz mithalten und eine Nachfrage unter meinen Followern auf Twitter bzgl. Erfahrungen mit Hosteurope ergab durchweg positive Resonanz. Die einzige offene Frage, welche mir die Produktseiten nicht beantworten konnten ist, ob bei der Nameserver-Konfiguration das angeben externer Nameserver möglich ist. Dies ist für die Verwendung von Diensten wie Cloudflare unbedingt erforderlich und könnte für mich unter Umständen ein Ausschlusskriterium sein. Vor kurzem habe ich eine Domain von Strato zu einem anderen Domain-Hoster umziehen müssen, da bei Strato dies nicht ohne weiteres möglich ist.

Falls Ihr Anmerkungen oder Erfahrungen zu Hosteurope habt, würde ich mich freuen, wenn Ihr diese in den Kommentaren mit mir teilt.

Der Beitrag Das Portfolio von Hosteurope unter die Lupe genommen erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2013/06/01/das-portfolio-von-hosteurope-unter-die-lupe-genommen/feed/ 5
Apache access.log mit node.js in Realtime parsen und zur Weiterverabeitung in RabbitMQ queuen https://www.feldstudie.net/2013/05/17/apache-access-log-mit-node-js-in-realtime-parsen-und-zur-weiterverabeitung-in-rabbitmq-queuen/ https://www.feldstudie.net/2013/05/17/apache-access-log-mit-node-js-in-realtime-parsen-und-zur-weiterverabeitung-in-rabbitmq-queuen/#comments Fri, 17 May 2013 06:54:23 +0000 http://www.feldstudie.net/?p=3817 Für ein firmeninternes Projekt (nein, hier werden keine ‚Internals‘ preisgegeben 😉 ) werte ich Apache access.log Dateien aus. Dies geschieht aktuell mittels eines Cronjobs, welcher ein PHP Script startet und das Log Zeile für...

Der Beitrag Apache access.log mit node.js in Realtime parsen und zur Weiterverabeitung in RabbitMQ queuen erschien zuerst auf Feldstudie.

]]>
Für ein firmeninternes Projekt (nein, hier werden keine ‚Internals‘ preisgegeben 😉 ) werte ich Apache access.log Dateien aus. Dies geschieht aktuell mittels eines Cronjobs, welcher ein PHP Script startet und das Log Zeile für Zeile ausliest, die Daten via Regular Expressions und StringSplit parsed und anschließend in eine MySQL-Datenbank schreibt. Das Problem hierbei ist, dass sowohl die Dateien (Logrotation ist auf 15 Minuten eingestellt) als auch die Zeilen seriell bearbeitet werden und dies natürlich sehr lange dauert. Zwischenzeitlich habe ich mir bereits gearman angeschaut, um trotz der Verwendung mit PHP diese Prozesse zu parallelisieren, jedoch habe ich hierbei bereits nach kurzer Zeit die Flinte ins Korn geworfen.

Nachdem ich mich nun mittlerweile seit einiger Zeit mit Node.js beschäftigt habe und nach einer knappen Woche auch gut mit RabbitMQ und dem Node.js-Modul node-amqp zurechtkomme, kam mir eine neue Idee. Warum nicht das access.log in Echtzeit auslesen, neu hinzugekommene Zeilen in eine RabbitMQ Queue zu schreiben und dann mit mehreren Worker-Prozessen parallel zu parsen und in eine Datenbank zu schreiben. Somit müsste nicht mehr auf die nächste Log-Rotation (15 min) und die Fertigstellung des Parsing-Scripts gewartet werden und es stünden die Daten nahezu in Echtzeit zur Verfügung.

Hier also die ersten Schritte, um das neue Konzept umzusetzen:

Schritt 1 – Vorbereitung

Zuerst laden wir die Module, welche für das Projekt benötigen:

var fs = require('fs');		// Zugriff auf das Dateisystem
events = require('events');	// Erstellung eigener Events
util = require('util');		//
async = require('async');	// asynchrone Schleifen, etc.
amqp = require('amqp');		// Modul für Verbindung zu RabbitMQ
sys = require('sys');		// Zugriff auf das System
spawn = require('child_process').spawn;	// Erstellung von Child-Prozessen

Schritt 2 – vorbereiten und erstellen der Verbindung zu RabbitMQ

Da die Informationen aus dem access.log direkt in eine Queue geschrieben werden sollen, um sie später parallel von mehreren Workern verarbeiten zu können, muss zuerst eine Verbindung mit RabbitMQ hergestellt werden, welcher diese Queues verwaltet.

var defaultExchange = 'amq.topic';
var rabbitMQ = amqp.createConnection({
	host: 'hostname.example.com',
	login: 'user-publisher',
	password: 'superGeheim',
	vhost: '/'
}, {
	defaultExchangeName: defaultExchange
});

Den

defaultExchangeName
  habe ich bewusst ein eine gesonderte Variable geschrieben, da später noch die Queue an den Exchange gebunden werden muss und dort der Name erneut benötigt wird. Darüber hinaus wird lediglich die Verbindung mit den entsprechenden Benutzerdaten zu dem angegebenen Host aufgebaut.

Schritt 3 – Überprüfen der Verfügbarkeit und Erstellen der Queue

Wir warten auf das ‚ready‘-Event der Verbindung zu RabbitMQ und legen anschließend die notwendigen Parameter für die von uns benötigte Queue fest, falls diese nicht vorhanden sein sollte und erstellt werden muss. Sollte diese nicht bereits vorhanden sein, wird sie mit den von uns vorgegebenen Werten erstellt, ansonsten wird die vorhandenen Queue genutzt, wie sie beim Erstellen konfiguriert wurde. Ist die Überprüfung bzw. die Erstellung abgeschlossen, emitten wir das Event ‚ready‘, um mit dem Einlesen des Logs zu beginnen.

rabbitMQ.on('ready', function() {

	var queueOptions = {
		durable: true,
		autoDelete: false
	};
	var queueName = 'log-test';

	var q = rabbitMQ.queue(queueName, queueOptions, function(queue) {
		console.log('queueName ' + queueName + ' has been created');
		q.emit('ready');
	});
	util.inherits(q, events.EventEmitter);
});

Schritt 4 – Einlesen des access.log in Echtzeit

Ich habe mir einige Module angeschaut, welche dabei helfen sollen, Dateien kontinuierlich einzulesen, welche von einem fremden Prozess permanent erweitert werden. Dazu gehörten u.a. fs.watchchokidar und carrier. Leider half mir keines bei meinem Problem. Ich habe mich daher entschlossen, auf das Tool ‚tail‘ zurückzugreifen, welches mittels des Parameters -f kontinuierlich die letzten Zeilen des Logs auf der Standardausgabe ausgibt. Dieses Programm ist normalerweise nur unter Linux standardmäßig vorhanden, jedoch gibt es auch seit einiger Zeit eine entsprechende Portierung für Windows. Tail wird daher für unsere Zwecke in einem Child-Prozess gestartet und die Ausgabe als Buffer-Stream weiterverarbeitet.

Im ersten Schritt habe ich den Buffer in einen UTF-8 String umgewandelt und diesen über die Newline-Zeichen gesplittet, um für jede Zeile des Logs einen eigenen String zu erhalten. Bei einer Erweiterung des Scripts durch Worker-Prozesse, welche die Hauptaufgabe des Parsens übernehmen sollen, wäre zu überlegen, ob es nicht sinnvoller und schneller wäre, erst dort der Buffer zu splitten. Aktuell bleiben wir aber erstmal bei der vorhandenen Lösung.

Für das Durchlaufen der einzelnen Felder des Arrays verwende ich das ‚async‘-Modul für Node.js, welches das asynchrone bearbeiten der Felder ermöglicht, so dass hier ein weiterer Performancegewinn zu verzeichnen ist. Dabei wird für jeden String überprüft, ob dieser leer ist und sollte dies nicht der Fall sein, wird dieser über rabbitMQ.publish und den entsprechenden defaultExchange an die Queue gesendet.

q.on('ready', function() {
	q.bind(defaultExchange, q.name);
	console.log('ready event for queue triggered');
	var file = "d:/xampp/apache/logs/access.log";
	var tailpath = 'tail.exe';
	var tail = spawn(tailpath, ["-f", file]);
	tail.stdout.on('data', function(data) {
		var buff = new Buffer(data);
		var logLine = buff.toString('utf8');
		var splitByNewLines = logLine.split(/\r\n/g);
		async.each(splitByNewLines, function(item, callback) {
			if (item === '') {
				console.log('empty');
			} else {
				rabbitMQ.publish(q.name, item);
			}
		}, function(err) {
			console.log(err);
		});
		console.log(splitByNewLines);
	});
});

Schritt 5 – Überprüfen der Ergebnisse in RabbitMQ

Wenn wir uns die Konsole und die entsprechende Queue in RabbitMQ ansehen, können wir beobachten, wie die Zugriffe auf den Webserver in Echtzeit ausgelesen und gequeued werden:

 

 

Der Beitrag Apache access.log mit node.js in Realtime parsen und zur Weiterverabeitung in RabbitMQ queuen erschien zuerst auf Feldstudie.

]]>
https://www.feldstudie.net/2013/05/17/apache-access-log-mit-node-js-in-realtime-parsen-und-zur-weiterverabeitung-in-rabbitmq-queuen/feed/ 3