Apfel-Appstore-Reader - Update 2.0

Wie im Artikel - Apfel-Appstore-Reader - beschrieben, hält sich Apple bezüglich der App-Rankings (Positionierung im Appstore) sehr gedeckt. Auch im Report-Tool (ITC / iTunes-Connect -> Sales & Trends) keinerlei Informationen auftauchen, an welcher Position man sich mit der jeweiligen App in z.B. einer Kategorie wie “Produktivität” befindet, habe ich vor längerer Zeit ein kleines Java-Tool (mit dem einfallsreichen Namen Appstore-Reader) geschrieben.

amz box.jpgDie nakten Zahlen des Tools sind ganz nett, aber leider wenig aussagekräftig... Zum Einen variiert das App-Ranking über den Tag teils erheblich, zum Anderen hat man wohl kaum lust das Tool ständig aufzurufen, um sich die Werte dann irgendwo aufzuschreiben.

Eine Datenbank-Anbindung an MySQL MySQL sowie lokal an eine SQLite-DB hatte ich zwar realisiert, um mal eine Durchschnittsposition über den Monat hinweg zu errechnen, aber optimal und zufriedenstellend war das Ganze immer noch nicht.

Inhaltsverzeichnis:

Überlegungen zur Optimierung des Store-Readers

  • Auslagerung des Java-Tools vom lokalen Heim-Rechner auf einen Server im Internet.
  • Das Tool sollte selbstständig z.B. einmal pro Stunde gesteuert über einen Cron-Job alle Kategorien des Appstores durchsuchen und die Rankings der gewünschten Apps speichern.
  • Es muss eine Schnittstelle her, um aus den erfassten Daten eine grafische Übersicht zu zaubern. Tages- und Monatsübersicht -> zu welchen Zeiten finden die meisten Verkäufe/Downloads statt bzw. ist das App-Ranking am höchsten?!

Die Umsetzung - Java, PHP und JPGraph

appstore_reader_2_previewAusgangssituation ist der Apfel-Appstore-Reader. Lediglich die statischen Arrays wie die Länderkürzel, Appstore-Genres und die Links zum Appstore-RSS-Generator habe ich in die neue MySQL-Datenbank ausgelagert um etwas flexibler zusein, grade was das spätere Auswerten der Daten angeht. Aus dem Java-Code braucht man dann nurnoch ein Jar-File erstellen, auf den Server laden und einen Cron einrichten. Ich habe mich für einen stündlichen Cronjob entschieden, so soll man später Stundenweise sehen wie sich das App-Ranking verhält.

Auszug aus der Crontab - 10 Minuten nach jeder vollen Stunde von 9 bis 23 Uhr:
10 9-23 * * * root java -jar /var/www/myweb/httpdocs/java/sync.jar > /dev/null

christ schmuck.jpgDie Software speichert nun stündlich anhand der App-ID den aktuellen App-Rang und folgende Informationen. Felder: app_id, datum, uhrzeit, kategorie_id (ID des Appstore-Genres, z.B. 6002 für Dienstprogramme), store_kategorie_id (eindeutige Kennung, z.B. 1 = Top Apps Kostenlos, 2 = Top Apps iPad, 3 = Top Apps Umsatzstark, usw....), rank (Position der App im Store), store_lang (Appstore, z.B. de für Deutschland).

Jetzt wird es Zeit die Daten in Form zubringen. :) Ich habe mich für die OpenSource-Lösung JPGraph entschieden, um die erfassten App-Ranking-Daten visuell aufzubereiten. Mit diesem PHP-Framework kann man schnell tolle Grafiken erstellen. JPGraph ist sehr mächtig und verfügt über unzählige Anpassungsmöglichkeiten, daher gehe ich hier nur auf einige Basics ein. ;-)

Zunächst holt man sich die Library per include bzw. require in seine PHP-Funktion.
require_once ($jpgraph_path.'/src/jpgraph.php');
require_once ($jpgraph_path.'/src/jpgraph_line.php');

Jetzt erstellt man den Graphen, im Beispiel soll die Grafik 800x500 Pixel groß werden, und anhand einer Kurve den Ranking-Verlauf über den Tag hinweg zeigen. Eine Legende unter der Grafik soll helfen das gezeigte auch zu verstehen.

$graph = new Graph(800,500);
$graph->legend->Pos(0.00,0.5,"center","bottom");
$graph->legend->SetLayout(LEGEND_HOR);
$graph->legend->SetFont(FF_FONT1, FS_NORMAL);
$graph->SetScale("textlin");
$graph->SetAxisStyle(AXSTYLE_BOXOUT);

Nun fügen wir ein bischen Margin, also freie Fläche rund um die Grafik ein, damit das Ganze nicht ganz so gedrückt aussieht. Dann fügen wir eine Überschrift (Ranking + App-Name) über das Diagramm ein, und etwas kleiner darunter das Datum der gezeigten Daten inkl. Appstore-Kategorie und Land/Sprache des Stores. Dann noch die Beschriftung für die X- und Y-Achse.

$graph->img->SetMargin(70,70,80,30);

$graph->title->Set("Ranking - ".$app_name);
$graph->title->SetFont(FF_FONT1,FS_BOLD);
       
$graph->subtitle->Set($this->getDateLongFromSqlDate($sqldate)." - ".$kategorie_name." / ".$lang);
$graph->subtitle->SetFont(FF_FONT1,FS_NORMAL);

$graph->yaxis->SetTitle("Rang");
$graph->xaxis->SetTitle("Uhrzeit");

An dieser Stelle laden wir die erfassten Daten in ein Array, woraus dann die entsprechenden Kurven erstellt werden. $dataLabels enthält die Punkte auf der X-Achse (Uhrzeiten: 8 bis 22 Uhr). $datay ist ein mehrdimensionales Array und behinhaltet alle Y-Daten (App-Store-Positionen zur entsprechenden Uhrzeit). Da in meinem Fall bis zu 6 Store-Sektionen angezeigt werden sollen (Top-Apps, Top-Apps iPad, usw...) speichere ich die IDs und Namen in separaten Arrays ($stores und $store_kategorie_ids). Die Daten aus Sektion 0 (z.B. Top-Apps mit ID 1) werden dann in $datay[0] gespeichert, Sektion 1 in $datay[1] etc... Auf den ersten Blick etwas wirsch, auf den Zweiten sehr logisch und schön wie ich finde. ;-)

$dataLabels = array();
$count = 0;
       
for ($i=8; $i<=22; $i++)
{
               for ($s=0; $s<$store_count; $s++)
               {
                   $q = "SELECT rank FROM ".TABLE_APPS_RANKING." WHERE app_id='".$id."'"
                    ." AND store_kategorie_id='".$store_kategorie_ids[$s]."' AND store_lang='".$lang."'"
                       ." AND kategorie_id='".$kategorie_id."'"
                       ." AND datum='".$sqldate." ".$i.":00:00'";
                   $res = $this->query($q);
                                                                       
                   if ($data = mysql_fetch_assoc($res))
                   {
                       if (!$has_data)
                           $has_data = true;
                   }
                   else
                   {
                       $data = array();
                       $data['rank'] = '';
                   }
                   $datay[$s][$count] = $data['rank'];
               }
       
               $dataLabels[$count] = $i.":00";
               $count++;
}
$graph->xaxis->SetTickLabels($dataLabels);

Damit der bessere Rang oben steht und nicht unten müssen wir noch die Y-Achse spiegeln. Das funktioniert ganz einfach indem wir die Y-Werte ins Negative bringen.

for ($s=0; $s<$store_count; $s++)
{
    $n = count($datay[$s]);
    for($i=0; $i<$n; ++$i)
    {
         if ($datay[$s][$i]!="")
                $datay[$s][$i] = round(-$datay[$s][$i]);
    }
}

if (!function_exists("_cb_negate"))
{
     function _cb_negate($aVal)
    {  return round(-$aVal);  }
}

$graph->yaxis->SetLabelFormatCallback("_cb_negate");

Abschließend werden noch die entsprechenden Kurven (LinePlot) mit Hilfe der Y-Daten erstellt und in den Graphen geladen. Ein kleines Array namens $colors mit Farbwerten hilft, die einzelnen Linien zu unterscheiden. ->setLegend(titel) darf nicht fehlen, um die Beschreibung dieser der Legende hinzuzufügen. Alle anderen Aufrufe wurden aufs wesentliche runtergebrochen und sollten selbsterklärend sein. :-)

$colors = array("#00FF00", "#0000FF", "#FF0000", "#00FFFF", "#FFFF00", "#FF00FF");
for ($i=0; $i<$store_count; $i++)
{
               $p1 = new LinePlot($datay[$i]);
               $graph->Add($p1);
                                                       
               $p1->SetLegend($stores[$i]);
                                                       
               $p1->mark->SetType(MARK_SQUARE, 1.0);
               $p1->mark->SetWidth(3);
               $p1->mark->SetFillColor($colors[$i]);
                                                  
               $p1->SetColor($colors[$i]);
               $p1->SetWeight(2);                                                                             
               $p1->SetStepStyle(true);
}

Jetzt noch $graph->Stroke() aufrufen, um die Grafik zu erstellen. Als Parameter wird der Pfad übergeben, wohin die Datei gespeichert werden soll. Alternativ kann man die Funktion Stroke() auch ohne Parameter aufrufen, was zur Folge hat, dass die Grafik direkt zurückgeben wird. Funktioniert auch wenn man das PHP-Script einzig zum Generieren nutzt und die Grafik mit z.B.  <img src="meinscript.php"> aufrufen will.

Grafik als Datei speichern: $saveas z.B. ./images/grafik.png
$graph->Stroke($saveas);

Das Resultat - Apfel-Appstore-Reader 2.0

Das Resultat ist eine kleine ansehnliche Grafik des App-Rankings der jeweiligen App.

appstore_reader_2_screen

Passt man das Script hier und da etwas an, kann man ganz einfach auch mehrere Apps mit einander vergleichen, oder Monats- und Jahresübersichten visualisieren.

Grafiken / Reports automatisiert per E-Mail verschicken

Kaum jemand hat lust sich jeden Tag in sein Backoffice einzuloggen um einzelnt die Reports durchzuklicken. Daher hier noch kurz ein Beispiel für den Mailversand der Grafiken bzw. Berichte. Mit Hilfe von PHP und eines Crons am morgen kein Problem! Alles was wir brauchen ist eine Funktion der wir die E-Mail-Adresse des Empfängers, einen Betreff, eine Nachricht und ein Array mit den Pfaden zu den erstellten Reports (PNG-Files). Das Script sollte selbsterklärend sein.:)

function sendMailWithAttachment($email, $subject, $msg, $files=array())
   {
           $nl = "\r\n";
           
           $header = "From: ".ADMIN_EMAIL;
           $boundary = strtoupper(md5(uniqid(time())));
       
           $header .= "\nMIME-Version: 1.0";
           $header .= "\nContent-type: multipart/mixed; boundary=".$boundary;
           $header .= "\n\nThis is a multi-part message in MIME format  --  Dies ist eine mehrteilige Nachricht im MIME-Format";
       
           /* mail-inhalt */
           $header .= $nl."--".$boundary;
           $header .= $nl."Content-type: text/html";
           $header .= $nl."Content-Transfer-Encoding: 8bit";
           $header .= $nl.$nl.$msg;
       
           /* anhänge anfügen */
           for ($i=0; $i<count($files); $i++)
           {
               $file = $files[$i];
           
               if (file_exists($file))
               {
                   $inhalt = $this->read_file($file);
                   
                   //nur png-files unterstützt; ggf. noch content-types einfügen; jpg, gif, pdf, etc...
                   $filename = "anhang".($i+1).".png";
                   
                   $header .= $nl."--".$boundary;
                   $header .= $nl."Content-type: images/png; name=\"".$filename."\"";
                   $header .= $nl."Content-Transfer-Encoding: base64";
                   $header .= $nl."Content-Disposition: attachment; filename=\"".$filename."\"";
                   $header .= $nl.$nl.$inhalt;
               }
           }
           
//ende der mail
$header .= $nl."--".$boundary."--";
           
//senden
mail($email, $subject, "", $header);

Schlusswort

Interessant was man so über die eigene App und Zielgruppe erfährt. Eine der Überlegungen wäre, wenn die meisten Downloads der App z.B. zwischen 19 und 21 Uhr gefahren werden, könnte man seine Facebook-Werbekampagnen genau auf diese Stoßzeiten legen, um das Ranking noch weiter in die Höhe zu treiben, anstatt plätscherweise sein Budget über den Tag zu verschleudern. ;-) *Nur ein Gedanke*

Hoffe das Lesen hat Spaß gemacht, Feedback und Anregungen erwünscht! :-)

Author: Sascha von Saschas-Bastelstube.de

Autor: sascha

Ein paar Worte über mich selbst. Mein Name ist Sascha, wie der Titel dieses Blogs erahnen lässt. :-) Ich bin von Beruf selbstständiger Programmierer und Trader. Neben Programmierung, Trading und Kampfsport sind einige meiner Interessen und Hobbys das Kochen, Fitness und das Hören lauter Heavy-Metal Musik. :D

Dies könnte dich auch interessieren

Programmieren: PHP image captcha code

Hier möchte ich meine kleine aber feine capture code Lösung vorstellen, welche ich vor einiger Zeit mal programmiert habe. Ich war es leid, auch in Hinblick auf die DSGVO, Capture-Lösungen externer Anbieter einzubinden. Darüber hinaus werden durch diverse Privacy-Browser z.B. auch Javascripts geblockt, sodass ein externes Image-Capture-Modul für etwa das eigene Kontakt- oder Anmeldeformular nicht … „Programmieren: PHP image captcha code“ weiterlesen


Hinzufügen von Website-Icons für iPhone, iPad und Android-Geräte

So ziemlich jeder Webseitenbetreiber hinterlegt im Wurzelverzeichnis seiner Webseite ein Favicon. Diese kleine Grafik wird dann im Browser-Tab oben neben dem Namen der Webseite angezeigt, genauso wie beim setzen eines Bookmarks bzw. Lesezeichens. Manch einer vergisst jedoch, dass inzwischen etliche Benutzer vorwiegend mit Mobilgeräten bzw. Smartphones wie iPhone, iPad oder Android-Smartphones und Tablets im Internet … „Hinzufügen von Website-Icons für iPhone, iPad und Android-Geräte“ weiterlesen


iTunes-Connect - Sales-Reports

Wer Apple und iTunes-Connect (ITC) kennt, weiß von den Problemchen welche ITC teilweise mit sich bringt. Mich persönlich hat immer gestört, das die Sales-Reports / Verkaufszahlen des Vortages manchmal erst am späten Abend des Folgetages verfügbar waren. Das liegt wohl daran, das die Apple-Server die Reports für jeden Entwickler aufbereiten müssen und es hier öfter … „iTunes-Connect - Sales-Reports“ weiterlesen


Der Apfel-Appstore-Reader

Aufgrund der Tatsache das Apple sich bezüglich der App-Rankings (Positionierung im Appstore) sehr gedeckt hält, und auch im Report-Tool (ITC / iTunes-Connect -> Sales & Trends) keinerlei Informationen auftauchen, an welcher Position man sich mit der jeweiligen App in z.B. einer Kategorie wie "Dienstprogramme" befindet, habe ich ein kleines Java-Tool (mit dem einfallsreichen Name Appstore-Reader) … „Der Apfel-Appstore-Reader“ weiterlesen


Experiment: Hacking Ethereum Wallets - Bruteforce

Vor einiger Zeit bin ich durch Zufall auf ein Youtube-Video gestoßen mit dem Titel: "Eth Wallet Bruteforce Hack". Ein Programmierer zeigte da ein einfaches Python Script welches einen zufälligen Private-Key (64 Zeichen / 32 Byte / 256 Bit) generieren konnte. Daraus wurde Public-Key und Adresse errechnet und an einen Blockchain-Explorer-Webseite wie Etherscan.io übergeben ob diese … „Experiment: Hacking Ethereum Wallets - Bruteforce“ weiterlesen