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.

Die 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 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.

Ü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

Die 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! :-)

Zuletzt bearbeitet: 19. April, 2016
Tags: , , , , , , , , ,

Weitere interessante Beiträge

Einkaufszettel-App jetzt auch für Apple-Watch

Produkte manuell oder aus dem integrierten Bestand hinzufügen (es sind über 2000 in 17 Hauptkategorien), nach Abteilungen deines Marktes sortieren und schon kann dein Einkauf los gehen. Geräteübergreifend synchronisiert wird zumindest unter iOS und MacOSX vollautomatisch über iCloud. Die Einkaufszettel-App gibt es neben iOS, MacOSX und Android nun auch für deine Apple Watch. So hast [...] 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 [...] Weiterlesen »


Nützliche Apps für deinen Instagram-Account

Like4Like Pro - Erhalte mehr likes, views und follower bei Instagram. Like4Like PRO - get more likes, video views and followers for Instagram. A dedicated Like4Like user can get over 5000 Likes and 1000 Followers on Instagram per day! Try it out, now! There is no trick, you are going to get real Likes and [...] 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+ . Ziel war es, sich in einem Raum zu bewegen bzw. allein durch das Ansehen von Objekten mit diesen zu interagieren, ohne [...] Weiterlesen »


App des Tages: Super Mario Run

In diesem Spiel bewegt sich Mario durch die jeweiligen Level vorwärts und du lenkst ihn mithilfe diverser Sprünge in Richtung Ziel. Marios Verhalten ist dabei vom Timing deiner Berührungen des Bildschirms abhängig. Dein Geschick entscheidet, ob es dir gelingt, Münzen zu sammeln, das Ziel zu erreichen und Mario dabei auch noch gut aussehen zu lassen. [...] Weiterlesen »