Seite 1 von 1

Projekt um Programme Automatisch mittels APIs zu Aktualliesieren

Verfasst: Dienstag 16. Juni 2020, 08:58
von krusty
Hallo ich bin Umschüler zum Anwendungsentwickler und zur Zeit im Praktikum, in der Schule hatten wir nur JAVA als Programmiersprache und jetzt im Betrieb soll ich Python benutzen, wobei ich sagen muss das Python eine echt tolle Sprache ist. Als Projekt soll ich ein Programm schreiben in dem 2 Programme Daten automatisch, mittels Json Dateien, über APIs an ein drittes Programm schicken und Aktualisieren. In dem Betrieb in dem ich bin ist es leider so das hier kein Entwickler oder irgendwer der sich mit Programmierung auskennt ist und ich bei fragen nur zu hören bekomme, guck bei Google oder sowas wie, wenn ich dir das sage kann ich es auch gleich selber machen. Ich hab soweit ein Programm das die Dateien holt und auch versendet zusammen geschustert. Jetzt soll ich Funktionen einbauen die beim anlegen eines neuen Kunden Automatisch die Daten holt und an unser CRM sendet und das selbe beim anlegen eines neuen Tickets. Genau da komme ich jetzt nicht weiter.

Zuerst der Code ohne Funktionen, da empfange und sende ich die Daten und die kommen auch beim Empfänger an

Code: Alles auswählen

import json
import requests
from requests.auth import AuthBase

class TokenAuth(AuthBase):

    """Funktion zum Generieren eines Tokens """

    def __init__(self, token): 
        self.token = token
        self.token1 = token

    """ Funktion zur Authentifizierung. Der Header wird Generiert und an die URL gesendet"""

    def __call__(self, r):
        r.headers['X-Ninja-Token'] = f'{self.token}' 
        r.headers['Authorization'] = f'{self.token1}' 
        return r
            

response = requests.get('https://xxx/api/v1/clients?invoice_id=2', auth=TokenAuth(xxx))
data = response.json()
print(json.dumps(data, indent= 4))
response1 = requests.get('https://xxx/api/v1/tickets', auth=TokenAuth('Token token=xxx'))
data1 = response1.json()
print(json.dumps(data1, indent=4))


a = open("jason.txt", "r+")
a.write(str(data))
a.write(str(data1))
a.close()


data1 = requests.post('https://xx/api/v1/clients?invoice_id2', auth=TokenAuth(xxx))
print(data1)

und nun der mit Funktionen der leider nicht Funtioniert

import json
import requests
from requests.auth import AuthBase

class TokenAuth(AuthBase):

    """Funktion zum Generieren eines Tokens """

    def __init__(self, token): 
        self.token = token
        self.token1 = token

    """ Funktion zur Authentifizierung. Der Header wird Generiert und an die URL gesendet"""

    def __call__(self, r):
        r.headers['X-Ninja-Token'] = f'{self.token}' 
        r.headers['Authorization'] = f'{self.token1}' 
        return r

    """ Funktion zum weiterleiten von neu erstellten Clients """

    def new_client(self, clients):
        for client in clients():
            if client > 0:
                response = requests.get('https://xxxxxx/api/v1/clients?invoice_id=2', auth=TokenAuth(xxxxxx))
                data = response.json()
                data = requests.post('https://xxxxxx/api/v1/clients?invoice_id2', auth=TokenAuth(xxxxxx))
                print(json.dumps(data, indent= 4))
                return data

    """ Funktion zum weiterleiten von neu erstellten Tickets """

    def new_ticket(self, tickets):
        for ticket in tickets():
            if ticket > 0:
                response1 = requests.get('https://xxxxxx/api/v1/tickets', auth=TokenAuth('Token token=xxxxxx))
                data1 = response1.json()
                data1 = requests.put('https://xxxxxx/api/v1/clients?invoice_id2', auth=TokenAuth(xxxxxx))
                print(json.dumps(data1, indent=4))
                print(data1)

Re: Projekt um Programme Automatisch mittels APIs zu Aktualliesieren

Verfasst: Dienstag 16. Juni 2020, 10:32
von Sirius3
Diese komischen Strings zwischen den Methoden-Definitionen sollten wohl als Dok-Strings in den Methoden stehen.
Ein "funktioniert nicht" ist keine gute Fehlerbeschreibung. Was passiert konkret: was sollte eigentlich passieren, und was passiert statt dessen? Gibt es eine Fehlermeldung?
Da Du die Antwort auf die Requests nicht prüfst, kann es schwierig werden, Fehler zu suchen; eine Fehlerprüfung solltest Du also dringend einbauen.

Es ist seltsam, dass `tickets` aufgerufen werden muß. Was sind denn das für Objekte?
Was auch seltsam ist, ist dass eine Klasse TokenAuth Methoden new_ticket und new_client hat, und die selbst wieder TokenAuth-Instanzen erzeugen.

Re: Projekt um Programme Automatisch mittels APIs zu Aktualliesieren

Verfasst: Dienstag 16. Juni 2020, 10:41
von krusty
Wenn in unserem Ticketsystem ein neues Ticket erstellt wird dann soll das via API direkt an unser CRM gesendet werden und das selbe soll passieren wenn in der Zeiterfassung ein neuer Kunde angelegt wird. Meine Idee war das einfach mittels Threads laufen zu lassen so das sich das alle paar Sekunden Aktualisiert, was mein Chef allerdings nicht wollte. Deswegen hab ich die beiden Funktionen new_client und new_ticket versucht einzubauen was aber leider so gar nicht klappt. Es gibt zwar keine Fehlermeldung aus aber es tut sich halt auch gar nichts :(

Re: Projekt um Programme Automatisch mittels APIs zu Aktualliesieren

Verfasst: Dienstag 16. Juni 2020, 11:08
von Sirius3
Natürlich tut das nicht, wenn man irgendwo irgendetwas hinzuschreibt. Das muß auch an der richtigen Stelle aufgerufen werden. Nur in der TokenAuth-Klasse hat das nichts zu suchen.

Re: Projekt um Programme Automatisch mittels APIs zu Aktualliesieren

Verfasst: Dienstag 16. Juni 2020, 11:13
von __blackjack__
@krusty: Anmerkungen zum ersten Quelltext:

Die Docstrings stehen an der falschen Stelle. Der erste wird der Klasse zugeordnet, und der zweite ist an der Stelle wo er steht gar keine Docstring sondern einfach nur ein unnützer da nicht verwendeter Zeichenkettenwert.

Letztlich sind beide aber auch überflüssig denn was die beiden Methoden (nicht Funktionen) machen (sollen), kann man in der `requests`-Dokumentation nachlesen.

Es ist unsinnig den gleichen Wert an zwei Attribute zu binden. Ebenfalls keinen Sinn macht das Formatieren eines Wertes in eine Zeichenkette in der sonst nichts anderes steht. Da wäre ein `str()`-Aufruf einfacher und direkter.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Man nummeriert keine Namen. Man will dann entweder bessere Namen verwenden oder gar keine Einzelnamen sondern eine Datenstruktur. Oft eine Liste. Hier sind es aber bessere Namen. Namen sollen dem Leser verraten was der Wert dahinter im Programm bedeutet. `data` und `data1` tun das nicht wirklich. Und einbuchstabige Namen in der Regel auch nicht. Wenn einem bei einer Datei tatsächlich nichts besseres einfällt um den Inhalt oder Zweck der Datei zu beschreiben, dann nennt man die `file` und nicht `a`.

Bei den Antwortobjekten die `requests` liefert, sollte man auf den HTTP-Status aufpassen. Auch wenn es unwahrscheinlich ist, dass ein Status der nicht Ok ist im Körper etwas liefert was man als JSON dekodieren kann, wäre eine aussagekräftigere Ausnahme als ein `JSONDecodeError` wünschenswert. `Response`-Objekte haben da eine Methode für.

Für die Übergabe von URL-Parametern haben die `requests`-Funktionen ein eigenes Argument. Die würde ich nicht von Hand in die URL schreiben. Beim dritten Aufruf ist da ausserdem ein Fehler, denn zu einem Query-Argument gehört ein Wert. Da ist wohl irgendwie das "=" verloren gegangen. Die nächste Frage wäre ob das bei der ersten und der dritten Anfrage eigentlich die gleiche URL sein sollte‽ Unter anderem aus so einem Grund würde man so etwas wie diese URLs am Anfang des Quelltextes als Konstanten definieren.

Dateien öffnet man wo möglich mit der ``with``-Anweisung.

Der Dateimodus "r+" macht nie Sinn. Du willst hier die Datei zum Schreiben öffnen, das ist "w".

Bei Textdateien sollte man immer explizit eine Kodierung angeben. Im Falle von JSON-Daten ist das UTF-8.

Und dann muss man auch tatsächlich JSON in die Datei schreiben und nicht die Zeichenkettendarstellung von Python-Werten. Da gibt es überschneidungen zwischen Python-Syntax und JSON, aber das ist nicht das gleiche!

Wenn man mehr als einen Wert schreibt, dann ist das nicht mehr reines JSON sondern JSON-Lines. Und da muss man dafür sorgen, dass tatsächlich ein Zeilenendezeichen nach den einzelnen Schreibvorgängen geschrieben wird.

Aber JSON-Lines wird eher für Daten verwendet wo jede Zeile die gleiche Bedeutung hat. Man würde hier eher beide Antworten in ein neues JSON-Objekt verpackt, mit entsprechenden Schlüsseln, schreiben.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import json

import requests
from requests.auth import AuthBase

#
# TODO Need better names here.
#
SOME_API_URL = "https://xxx/api/v1/clients"
SOME_OTHER_API_URL = "https://xxx/api/v1/tickets"


class TokenAuth(AuthBase):
    def __init__(self, token):
        self.token = token

    def __call__(self, r):
        r.headers["X-Ninja-Token"] = str(self.token)
        r.headers["Authorization"] = str(self.token)
        return r


def main():
    parameters = {"invoice_id": "2"}
    response = requests.get(
        SOME_API_URL, params=parameters, auth=TokenAuth("xxx")
    )
    response.raise_for_status()
    invoice_data = response.json()
    print(json.dumps(invoice_data, indent=4))

    response = requests.get(
        SOME_OTHER_API_URL, auth=TokenAuth("Token token=xxx")
    )
    response.raise_for_status()
    tickets_data = response.json()
    print(json.dumps(tickets_data, indent=4))
    #
    # TODO Find a better filename.
    #
    with open("jason.txt", "w", encoding="utf-8") as file:
        json.dump(
            {"invoice_data": invoice_data, "tickets_data": tickets_data}, file
        )

    response = requests.post(
        SOME_API_URL, params=parameters, auth=TokenAuth("xxx")
    )
    response.raise_for_status()
    print(response)


if __name__ == "__main__":
    main()

Re: Projekt um Programme Automatisch mittels APIs zu Aktualliesieren

Verfasst: Dienstag 16. Juni 2020, 11:25
von krusty
@ __blackjack__ danke dir ich werd mir das merken! das sieht sehr viel besser aus als meins und funktioniert einwandfrei. die post Adresse ist nicht ganz gleich, hier laufen 2 Testversionen vom invoice und ich schick praktisch im Moment nur die Daten von einem zum anderen.