package com.mycompany.data;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.vaadin.data.Item;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.server.VaadinRequest;
import com.vaadin.util.CurrentInstance;
public class DataProvider {
public static Random rand = new Random();
/**
* Initialize the data for this application.
*/
public DataProvider() {
loadMoviesData();
loadTheaterData();
generateTransactionsData();
}
/**
* =========================================================================
* Movies in theaters
* =========================================================================
*/
/** Simple Movie class */
public static class Movie {
public final String title;
public final String synopsis;
public final String thumbUrl;
public final String posterUrl;
/** In minutes */
public final int duration;
public Date releaseDate = null;
public int score;
public double sortScore = 0;
Movie(String title, String synopsis, String thumbUrl, String posterUrl,
JsonObject releaseDates, JsonObject critics) {
this.title = title;
this.synopsis = synopsis;
this.thumbUrl = thumbUrl;
this.posterUrl = posterUrl;
this.duration = (int) ((1 + Math.round(Math.random())) * 60 + 45 + (Math
.random() * 30));
try {
String datestr = releaseDates.get("theater").getAsString();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
releaseDate = df.parse(datestr);
score = critics.get("critics_score").getAsInt();
sortScore = 0.6 / (0.01 + (System.currentTimeMillis() - releaseDate
.getTime()) / (1000 * 60 * 60 * 24 * 5));
sortScore += 10.0 / (101 - score);
} catch (Exception e) {
e.printStackTrace();
}
}
public String titleSlug() {
return title.toLowerCase().replace(' ', '-').replace(":", "")
.replace("'", "").replace(",", "").replace(".", "");
}
public void reCalculateSortScore(Calendar cal) {
if (cal.before(releaseDate)) {
sortScore = 0;
return;
}
sortScore = 0.6 / (0.01 + (cal.getTimeInMillis() - releaseDate
.getTime()) / (1000 * 60 * 60 * 24 * 5));
sortScore += 10.0 / (101 - score);
}
}
/*
* List of movies playing currently in theaters
*/
private static ArrayList<Movie> movies = new ArrayList<Movie>();
/**
* Get a list of movies currently playing in theaters.
*
* @return a list of Movie objects
*/
public static ArrayList<Movie> getMovies() {
return movies;
}
/**
* Initialize the list of movies playing in theaters currently. Uses the
* Rotten Tomatoes API to get the list. The result is cached to a local file
* for 24h (daily limit of API calls is 10,000).
*/
private static void loadMoviesData() {
File cache;
// TODO why does this sometimes return null?
VaadinRequest vaadinRequest = CurrentInstance.get(VaadinRequest.class);
if (vaadinRequest == null) {
// PANIC!!!
cache = new File("movies.txt");
} else {
File baseDirectory = vaadinRequest.getService().getBaseDirectory();
cache = new File(baseDirectory + "/movies.txt");
}
JsonObject json = null;
try {
// TODO check for internet connection also, and use the cache anyway
// if no connection is available
if (cache.exists()
&& System.currentTimeMillis() < cache.lastModified() + 1000
* 60 * 60 * 24) {
json = readJsonFromFile(cache);
} else {
// Get an API key from http://developer.rottentomatoes.com
String apiKey = "z8cmtwzsh6nhew268jq2s9hs";
json = readJsonFromUrl("http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?page_limit=30&apikey=" + apiKey);
// Store in cache
FileWriter fileWriter = new FileWriter(cache);
fileWriter.write(json.toString());
fileWriter.close();
}
} catch (Exception e) {
e.printStackTrace();
}
if (json == null) {
return;
}
JsonArray moviesJson;
movies.clear();
moviesJson = json.getAsJsonArray("movies");
for (int i = 0; i < moviesJson.size(); i++) {
JsonObject movieJson = moviesJson.get(i).getAsJsonObject();
JsonObject posters = movieJson.get("posters").getAsJsonObject();
if (!posters.get("profile").getAsString()
.contains("poster_default")) {
Movie movie = new Movie(movieJson.get("title").getAsString(),
movieJson.get("synopsis").getAsString(), posters.get(
"profile").getAsString(), posters.get(
"detailed").getAsString(), movieJson.get(
"release_dates").getAsJsonObject(), movieJson
.get("ratings").getAsJsonObject());
movies.add(movie);
}
}
}
/* JSON utility method */
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
/* JSON utility method */
private static JsonObject readJsonFromUrl(String url) throws IOException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is,
Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JsonElement jelement = new JsonParser().parse(jsonText);
JsonObject jobject = jelement.getAsJsonObject();
return jobject;
} finally {
is.close();
}
}
/* JSON utility method */
private static JsonObject readJsonFromFile(File path) throws IOException {
BufferedReader rd = new BufferedReader(new FileReader(path));
String jsonText = readAll(rd);
JsonElement jelement = new JsonParser().parse(jsonText);
JsonObject jobject = jelement.getAsJsonObject();
return jobject;
}
/**
* =========================================================================
* Countries, cities, theaters and rooms
* =========================================================================
*/
/* List of countries and cities for them */
static HashMap<String, ArrayList<String>> countryToCities = new HashMap<String, ArrayList<String>>();
static List<String> theaters = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
add("Threater 1");
add("Threater 2");
add("Threater 3");
add("Threater 4");
add("Threater 5");
add("Threater 6");
}
};
static List<String> rooms = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
add("Room 1");
add("Room 2");
add("Room 3");
add("Room 4");
add("Room 5");
add("Room 6");
}
};
/**
* Parse the list of countries and cities
*/
private static HashMap<String, ArrayList<String>> loadTheaterData() {
/* First, read the text file into a string */
StringBuffer fileData = new StringBuffer(2000);
BufferedReader reader = new BufferedReader(new InputStreamReader(
DataProvider.class.getResourceAsStream("cities.txt")));
char[] buf = new char[1024];
int numRead = 0;
try {
while ((numRead = reader.read(buf)) != -1) {
String readData = String.valueOf(buf, 0, numRead);
fileData.append(readData);
buf = new char[1024];
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
String list = fileData.toString();
/*
* The list has rows with tab delimited values. We want the second (city
* name) and last (country name) values, and build a Map from that.
*/
countryToCities = new HashMap<String, ArrayList<String>>();
for (String line : list.split("\n")) {
String[] tabs = line.split("\t");
String city = tabs[1];
String country = tabs[tabs.length - 2];
if (!countryToCities.containsKey(country)) {
countryToCities.put(country, new ArrayList<String>());
}
countryToCities.get(country).add(city);
}
return countryToCities;
}
/**
* =========================================================================
* Transactions data, used in tables and graphs
* =========================================================================
*/
/** Container with all the transactions */
private TransactionsContainer transactions;
public TransactionsContainer getTransactions() {
return transactions;
}
/** Create a list of dummy transactions */
private void generateTransactionsData() {
GregorianCalendar today = new GregorianCalendar();
/*
* Data items: timestamp, country, city, theater, room, movie title,
* number of seats, price
*/
transactions = new TransactionsContainer();
/* Amount of items to create initially */
for (int i = 1000; i > 0; i--) {
// Start from 1st of current month
GregorianCalendar c = new GregorianCalendar();
// we will go at most 4 months back
int newMonthSubstractor = (int) (5.0 * rand.nextDouble());
c.add(Calendar.MONTH, -newMonthSubstractor);
int newDay = (int) (1 + (int) (30.0 * rand.nextDouble()));
c.set(Calendar.DAY_OF_MONTH, newDay);
if (today.before(c)) {
newDay = (int) (1 + (int) (today.get(Calendar.DAY_OF_MONTH) * rand
.nextDouble()));
c.set(Calendar.DAY_OF_MONTH, newDay);
}
// Randomize time of day
c.set(Calendar.HOUR, (int) (rand.nextDouble() * 24.0));
c.set(Calendar.MINUTE, (int) (rand.nextDouble() * 60.0));
c.set(Calendar.SECOND, (int) (rand.nextDouble() * 60.0));
createTransaction(c);
// System.out.println(df.format(c.getTime()));
}
transactions.sort(new String[] { "timestamp" }, new boolean[] { true });
updateTotalSum();
}
private static double totalSum = 0;
private void updateTotalSum() {
totalSum = 0;
for (Object id : transactions.getItemIds()) {
Item item = transactions.getItem(id);
Object value = item.getItemProperty("Price").getValue();
totalSum += Double.parseDouble(value.toString());
}
/*
* try { Number amount = NumberFormat.getCurrencyInstance().parse( "$" +
* totalSum); totalSum = amount.doubleValue(); } catch (ParseException
* e) { e.printStackTrace(); }
*/
}
public static double getTotalSum() {
return totalSum;
}
private void createTransaction(Calendar cal) {
// Country
Object[] array = countryToCities.keySet().toArray();
int i = (int) (Math.random() * (array.length - 1));
String country = array[i].toString();
for (Movie m : movies) {
m.reCalculateSortScore(cal);
}
Collections.sort(movies, new Comparator<Movie>() {
@Override
public int compare(Movie o1, Movie o2) {
return (int) (100.0 * (o2.sortScore - o1.sortScore));
}
});
// City
ArrayList<String> cities = countryToCities.get(country);
String city = cities.get(0);
// Theater
String theater = theaters.get((int) (rand.nextDouble() * (theaters
.size() - 1)));
// Room
String room = rooms.get((int) (rand.nextDouble() * (rooms.size() - 1)));
// Title
int randomIndex = (int) (Math.abs(rand.nextGaussian()) * (movies.size() / 2.0 - 1));
while (randomIndex >= movies.size()) {
randomIndex = (int) (Math.abs(rand.nextGaussian()) * (movies.size() / 2.0 - 1));
}
if (movies.get(randomIndex).releaseDate.compareTo(cal.getTime()) >= 0) {
// System.out.println("skipped " + movies.get(randomIndex).title);
// System.out.println(df.format(movies.get(randomIndex).releaseDate));
// System.out.println(df.format(cal.getTime()));
// System.out.println();
// ++skippedCount;
// System.out.println(skippedCount);
return;
}
String title = movies.get(randomIndex).title;
// Seats
int seats = (int) (1 + rand.nextDouble() * 3);
// Price (approx. USD)
double price = (double) (seats * (6 + (rand.nextDouble() * 3)));
transactions.addTransaction(cal, country, city, theater, room, title,
seats, price);
// revenue.add(cal.getTime(), title, price);
}
public IndexedContainer getRevenueForTitle(String title) {
// System.out.println(title);
IndexedContainer revenue = new IndexedContainer();
revenue.addContainerProperty("timestamp", Date.class, new Date());
revenue.addContainerProperty("revenue", Double.class, 0.0);
revenue.addContainerProperty("date", String.class, "");
int index = 0;
for (Object id : transactions.getItemIds()) {
SimpleDateFormat df = new SimpleDateFormat();
df.applyPattern("MM/dd/yyyy");
Item item = transactions.getItem(id);
if (title.equals(item.getItemProperty("Title").getValue())) {
Date d = (Date) item.getItemProperty("timestamp").getValue();
Item i = revenue.getItem(df.format(d));
if (i == null) {
i = revenue.addItem(df.format(d));
i.getItemProperty("timestamp").setValue(d);
i.getItemProperty("date").setValue(df.format(d));
}
double current = (Double) i.getItemProperty("revenue")
.getValue();
current += (Double) item.getItemProperty("Price").getValue();
i.getItemProperty("revenue").setValue(current);
}
}
revenue.sort(new Object[] { "timestamp" }, new boolean[] { true });
return revenue;
}
public IndexedContainer getRevenueByTitle() {
IndexedContainer revenue = new IndexedContainer();
revenue.addContainerProperty("Title", String.class, "");
revenue.addContainerProperty("Revenue", Double.class, 0.0);
for (Object id : transactions.getItemIds()) {
Item item = transactions.getItem(id);
String title = item.getItemProperty("Title").getValue().toString();
if (title == null || "".equals(title))
continue;
Item i = revenue.getItem(title);
if (i == null) {
i = revenue.addItem(title);
i.getItemProperty("Title").setValue(title);
}
double current = (Double) i.getItemProperty("Revenue").getValue();
current += (Double) item.getItemProperty("Price").getValue();
i.getItemProperty("Revenue").setValue(current);
}
revenue.sort(new Object[] { "Revenue" }, new boolean[] { false });
// TODO sometimes causes and IndexOutOfBoundsException
if (revenue.getItemIds().size() > 10) {
// Truncate to top 10 items
List<Object> remove = new ArrayList<Object>();
for (Object id : revenue
.getItemIds(10, revenue.getItemIds().size())) {
remove.add(id);
}
for (Object id : remove) {
revenue.removeItem(id);
}
}
return revenue;
}
public static Movie getMovieForTitle(String title) {
for (Movie movie : movies) {
if (movie.title.equals(title))
return movie;
}
return null;
}
}