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

amz box.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

iOS 11 und das Redesign des Appstores

Neun Jahre ist es bereits her, dass Apple seinen App Store ins Leben gerufen hat. Eine riesige App-Plattform welche heutzutage über 2 Millionen Apps beheimatet. Nun wurde der Appstore komplett überarbeitet und neu designed. Die Änderungen und das neue Layout werden demnächst zusammen mit dem neuen Betriebssystem iOS11 eingeführt, welches aktuell als öffentliche Beta verfügbar … „iOS 11 und das Redesign des Appstores“ weiterlesen


iOS-Android-Converter

Lange Zeit habe ich bei neuen App-Projekten zunächst die Sprachdateien / Language-Files für iOS geschrieben, und diese dann für die Android-Version händisch umgeschrieben. Das war vom Aufwand her immer schnell gemacht solange die Anzahl der implementierten Sprachen und der Umfang der Textbausteine niedrig war. Abgesehen vom Zeitaufwand ist das ganz schön langweilige und stumpfe Arbeit, … „iOS-Android-Converter“ weiterlesen


VR Studie - Spielsteuerung ohne Controller

In den letzten Tagen habe ich mich etwas mit VR (Virtual Reality) und der Umsetzung einer Spielsteuerung beschäftigt, welche ohne Controller auskommt. Als Hardware diente die VR Box 3 (VR-Brille) sowie ein iPhone 6s+ . Inhaltsverzeichnis:VR Spielsteuerung ohne ControllerVR-Video bei Youtube - Demo - Spielsteuerung ohne ControllerVR BOX3 VR Spielsteuerung ohne Controller Ziel war es, sich … „VR Studie - Spielsteuerung ohne Controller“ weiterlesen


Trainingsgerät: Makiwara 2.0

Ein Makiwara ist ein aus Japan stammendes Sportgerät, welches man im Karate vor allem als Schlagpfosten aus Holz kennt. Früher (und zum Teil heute noch) fertigt man einen Makiwara aus einem biegsamen und nicht-splitternden Holzbrett. Das eine Ende des Brettes wird senkrecht in den Boden getrieben, und das andere Ende im oberen Bereich (ca. 10-20cm) … „Trainingsgerät: Makiwara 2.0“ weiterlesen


Telegram Messenger und die Bot API

Telegram ist im Prinzip ein Messenger ähnlich Whatsapp oder Threema , kostenlos und leicht zu bedienen. Ich möchte nicht sagen das dieser besser oder schlechter ist als andere Messenger, allerdings gefällt mir die Telegram Bot API sogut, dass ich an dieser Stelle ein kleines Tutorial dazu schreiben möchte. Inhaltsverzeichnis:Telegram Messenger Bot API programmierenSchritt 1 - … „Telegram Messenger und die Bot API“ weiterlesen