added examples
12
spring-data-neo4j-examples/cineasts/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
.DS_Store
|
||||
*.ipr
|
||||
*.iws
|
||||
*.iml
|
||||
*.zip
|
||||
data
|
||||
target
|
||||
*.log
|
||||
tmp
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
16
spring-data-neo4j-examples/cineasts/Readme.md
Normal file
@@ -0,0 +1,16 @@
|
||||

|
||||
Cineasts.net
|
||||
============
|
||||
|
||||
This project is the demo/tutorial application for the [Spring Data Graph](http://github.com/springsource/spring-data-graph) library which provides convenient access to the [Neo4j](http://neo4j.org) graph database.
|
||||
|
||||
This tutorial creates a complete web application built on top of Spring Data Graph.
|
||||
|
||||
It uses a domain that should be familiar to most - movies. So for cineasts.net we decided to add a social
|
||||
touch to the whole movie rating business, allowing friends to share their ratings and get recommendations
|
||||
for new friends and movies.
|
||||
|
||||
The tutorial is presented as a colloquial description of the steps necessary to create the application.
|
||||
It provides the configuration and code examples that are needed to understand what's happening.
|
||||
|
||||
The complete tutorial is contained in this projects [github wiki](https://github.com/jexp/cineasts/wiki).
|
||||
BIN
spring-data-neo4j-examples/cineasts/cineasts.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
325
spring-data-neo4j-examples/cineasts/pom.xml
Normal file
@@ -0,0 +1,325 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.neo4j</groupId>
|
||||
<artifactId>movies</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>Movies</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<spring.version>3.0.5.RELEASE</spring.version>
|
||||
<slf4j.version>1.6.1</slf4j.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<neo4j.version>1.3</neo4j.version>
|
||||
<spring-data-graph.version>1.0.0.RELEASE</spring-data-graph.version>
|
||||
<aspectj.version>1.6.11.RELEASE</aspectj.version>
|
||||
<org.slf4j-version>1.6.1</org.slf4j-version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-maven-release</id>
|
||||
<name>Spring Maven Release Repository</name>
|
||||
<url>http://maven.springframework.org/release</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-maven-snapshot</id>
|
||||
<name>Spring Maven Snapshot Repository</name>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<url>http://maven.springframework.org/snapshot</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-maven-milestone</id>
|
||||
<name>Spring Maven Milestone Repository</name>
|
||||
<url>http://maven.springframework.org/milestone</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>neo4j-public-repository</id>
|
||||
<url>http://m2.neo4j.org/</url>
|
||||
<name>Publicly available Maven 2 repository for Neo4j</name>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jboss-public-repo</id>
|
||||
<url>http://repository.jboss.org/maven2/</url>
|
||||
<name>JBoss public available repo</name>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-maven-release</id>
|
||||
<name>Spring Maven Release Repository</name>
|
||||
<url>http://maven.springframework.org/release</url>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-maven-milestone</id>
|
||||
<name>Spring Maven Milestone Repository</name>
|
||||
<url>http://maven.springframework.org/milestone</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${org.slf4j-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${org.slf4j-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>${org.slf4j-version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.16</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-neo4j</artifactId>
|
||||
<version>${spring-data-graph.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>opensymphony</groupId>
|
||||
<artifactId>sitemesh</artifactId>
|
||||
<version>2.4.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>4.0.2.GA</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet.jsp</groupId>
|
||||
<artifactId>jsp-api</artifactId>
|
||||
<version>2.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>jstl</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
<version>1.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>${aspectj.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib-nodep</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<version>1.0.0.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Neo4j Libraries -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.neo4j</groupId>
|
||||
<artifactId>neo4j-kernel</artifactId>
|
||||
<version>${neo4j.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>7.1.2.v20100523</version>
|
||||
<configuration>
|
||||
<webAppConfig>
|
||||
<contextPath>/</contextPath>
|
||||
</webAppConfig>
|
||||
<!--scanIntervalSeconds>1</scanIntervalSeconds -->
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!-- Must use java 1.5 or higher for annotations -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.1</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<useFile>true</useFile>
|
||||
<includes>
|
||||
<include>**/*Tests.java</include>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/Abstract*.java</exclude>
|
||||
</excludes>
|
||||
<junitArtifactName>junit:junit</junitArtifactName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>aspectj-maven-plugin</artifactId>
|
||||
<version>1.2</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>${aspectj.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjtools</artifactId>
|
||||
<version>${aspectj.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<outxml>true</outxml>
|
||||
<aspectLibraries>
|
||||
<aspectLibrary>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
</aspectLibrary>
|
||||
<aspectLibrary>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-neo4j</artifactId>
|
||||
</aspectLibrary>
|
||||
</aspectLibraries>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.neo4j.cineasts.controller;
|
||||
|
||||
|
||||
import org.neo4j.cineasts.service.CineastsUserDetailsService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* Handles and retrieves the login or denied page depending on the URI template
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/auth")
|
||||
public class AuthController {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(AuthController.class);
|
||||
|
||||
@Autowired
|
||||
CineastsUserDetailsService userDetailsService;
|
||||
@RequestMapping(value = "/login", method = RequestMethod.GET)
|
||||
public String login(@RequestParam(value = "login_error", required = false) boolean error, Model model) {
|
||||
logger.debug("Received request to show login page, error "+error);
|
||||
if (error) {
|
||||
model.addAttribute("error", "You have entered an invalid username or password!");
|
||||
}
|
||||
return "/auth/loginpage";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/register", method = RequestMethod.POST)
|
||||
public String register(
|
||||
@RequestParam(value = "j_username") String login,
|
||||
@RequestParam(value = "j_displayname") String name,
|
||||
@RequestParam(value = "j_password") String password,
|
||||
Model model) {
|
||||
|
||||
try {
|
||||
userDetailsService.register(login,name,password);
|
||||
return "forward:/user/"+login;
|
||||
} catch(Exception e) {
|
||||
model.addAttribute("j_username",login);
|
||||
model.addAttribute("j_displayname",name);
|
||||
model.addAttribute("error",e.getMessage());
|
||||
return "/auth/registerpage";
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/denied", method = RequestMethod.GET)
|
||||
public String denied() {
|
||||
logger.debug("Received request to show denied page");
|
||||
return "/auth/deniedpage";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/registerpage", method = RequestMethod.GET)
|
||||
public String registerPage() {
|
||||
logger.debug("Received request to show register page");
|
||||
return "/auth/registerpage";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.neo4j.cineasts.controller;
|
||||
|
||||
import org.neo4j.cineasts.movieimport.MovieDbImportService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@Controller
|
||||
public class ImportController {
|
||||
|
||||
private MovieDbImportService importService;
|
||||
private static final Logger log = LoggerFactory.getLogger(ImportController.class);
|
||||
|
||||
@Autowired
|
||||
public ImportController(MovieDbImportService importService) {
|
||||
this.importService = importService;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/import/{ids}", method = RequestMethod.GET)
|
||||
public String importMovie(@PathVariable String ids, Model model) {
|
||||
long start=System.currentTimeMillis();
|
||||
final Map<Integer, String> movies = importService.importMovies(extractRanges(ids));
|
||||
long duration = (System.currentTimeMillis() - start) / 1000;
|
||||
model.addAttribute("duration", duration);
|
||||
model.addAttribute("ids", ids);
|
||||
model.addAttribute("movies", movies.entrySet());
|
||||
return "import/result";
|
||||
}
|
||||
|
||||
|
||||
private Map<Integer, Integer> extractRanges(String ids) {
|
||||
Map<Integer, Integer> ranges = new LinkedHashMap<Integer, Integer>();
|
||||
StringBuilder errors = new StringBuilder();
|
||||
for (String token : ids.split(",")) {
|
||||
try {
|
||||
if (token.contains("-")) {
|
||||
String[] range = token.split("-");
|
||||
ranges.put(Integer.parseInt(range[0]), Integer.parseInt(range[1]));
|
||||
} else {
|
||||
int id = Integer.parseInt(token);
|
||||
ranges.put(id, id);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.append(token).append(": ").append(e.getMessage()).append("\n");
|
||||
}
|
||||
}
|
||||
if (errors.length() > 0) {
|
||||
throw new RuntimeException("Error parsing ids\n" + errors);
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package org.neo4j.cineasts.controller;
|
||||
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.neo4j.cineasts.domain.Person;
|
||||
import org.neo4j.cineasts.domain.Rating;
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.neo4j.cineasts.service.CineastsUserDetailsService;
|
||||
import org.neo4j.cineasts.service.DatabasePopulator;
|
||||
import org.neo4j.cineasts.service.CineastsRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@Controller
|
||||
public class MovieController {
|
||||
|
||||
private CineastsRepository moviesRepository;
|
||||
private CineastsUserDetailsService userDetailsService;
|
||||
private DatabasePopulator populator;
|
||||
private static final Logger log = LoggerFactory.getLogger(MovieController.class);
|
||||
|
||||
@Autowired
|
||||
public MovieController(CineastsRepository moviesRepository, DatabasePopulator populator, CineastsUserDetailsService userDetailsService) {
|
||||
this.moviesRepository = moviesRepository;
|
||||
this.populator = populator;
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
// for web service (JSON) clients
|
||||
|
||||
/**
|
||||
* Only matches 'GET /moviies/{id}}' requests for JSON content; a 404 is sent otherwise.
|
||||
* TODO send a 406 if an unsupported representation, such as XML, is requested. See SPR-7353.
|
||||
*/
|
||||
@RequestMapping(value = "/movies/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
|
||||
public
|
||||
@ResponseBody
|
||||
Movie getMovie(@PathVariable String id) {
|
||||
return moviesRepository.getMovie(id);
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/movies/{movieId}", method = RequestMethod.GET, headers = "Accept=text/html")
|
||||
public String singleMovieView(final Model model, @PathVariable String movieId) {
|
||||
User user = addUser(model);
|
||||
Movie movie = moviesRepository.getMovie(movieId);
|
||||
model.addAttribute("id", movieId);
|
||||
if (movie != null) {
|
||||
model.addAttribute("movie", movie);
|
||||
final int stars = movie.getStars();
|
||||
model.addAttribute("stars", stars);
|
||||
Rating rating = null;
|
||||
if (user!=null) rating = movie.getRelationshipTo(user, Rating.class, "RATED");
|
||||
if (rating == null) rating = new Rating().rate(stars,null);
|
||||
model.addAttribute("userRating",rating);
|
||||
}
|
||||
return "/movies/show";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/movies/{movieId}", method = RequestMethod.POST, headers = "Accept=text/html")
|
||||
public String updateMovie(Model model, @PathVariable String movieId, @RequestParam(value = "rated",required = false) Integer stars, @RequestParam(value = "comment",required = false) String comment) {
|
||||
Movie movie = moviesRepository.getMovie(movieId);
|
||||
User user = userDetailsService.getUserFromSession();
|
||||
moviesRepository.rateMovie(movie,user, stars==null ? -1 : stars,comment!=null ? comment.trim() : null);
|
||||
return singleMovieView(model,movieId);
|
||||
}
|
||||
|
||||
private User addUser(Model model) {
|
||||
User user = userDetailsService.getUserFromSession();
|
||||
model.addAttribute("user", user);
|
||||
return user;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/movies", method = RequestMethod.GET, headers = "Accept=text/html")
|
||||
public String findMovies(Model model, @RequestParam("q") String query) {
|
||||
List<Movie> movies = moviesRepository.findMovies(query, 20);
|
||||
model.addAttribute("movies", movies);
|
||||
model.addAttribute("query", query);
|
||||
addUser(model);
|
||||
return "/movies/list";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/actors/{id}", method = RequestMethod.GET, headers = "Accept=text/html")
|
||||
public String singleActorView(Model model, @PathVariable String id) {
|
||||
Person person = moviesRepository.getPerson(id);
|
||||
model.addAttribute("actor", person);
|
||||
model.addAttribute("id", id);
|
||||
addUser(model);
|
||||
return "/actors/show";
|
||||
}
|
||||
|
||||
//@RequestMapping(value = "/admin/populate", method = RequestMethod.GET)
|
||||
@RequestMapping(value = "/populate", method = RequestMethod.GET)
|
||||
public String populateDatabase(Model model) {
|
||||
Collection<Movie> movies=populator.populateDatabase();
|
||||
model.addAttribute("movies",movies);
|
||||
addUser(model);
|
||||
return "/movies/list";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/admin/clean", method = RequestMethod.GET)
|
||||
public String clean(Model model) {
|
||||
populator.cleanDb();
|
||||
return "movies/list";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/", method = RequestMethod.GET)
|
||||
public String index(Model model) {
|
||||
addUser(model);
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.neo4j.cineasts.controller;
|
||||
|
||||
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.neo4j.cineasts.service.CineastsRepository;
|
||||
import org.neo4j.cineasts.service.CineastsUserDetailsService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
/**
|
||||
* Handles and retrieves the login or denied page depending on the URI template
|
||||
*/
|
||||
@Controller
|
||||
public class UserController {
|
||||
|
||||
@Autowired CineastsUserDetailsService userDetailsService;
|
||||
@Autowired CineastsRepository repository;
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(UserController.class);
|
||||
|
||||
@RequestMapping(value = "/user", method = RequestMethod.GET)
|
||||
public String profile(Model model) {
|
||||
final User user = userDetailsService.getUserFromSession();
|
||||
model.addAttribute("user", user);
|
||||
model.addAttribute("recommendations", repository.recommendMovies(user, 3));
|
||||
return "/user/index";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/user/{login}/friends", method = RequestMethod.POST)
|
||||
public String addFriend(Model model, @PathVariable("login") String login) {
|
||||
userDetailsService.addFriend(login);
|
||||
return "forward:/user/"+login;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/user/{login}")
|
||||
public String publicProfile(Model model, @PathVariable("login") String login) {
|
||||
User profiled = userDetailsService.findUser(login);
|
||||
User user = userDetailsService.getUserFromSession();
|
||||
|
||||
return publicProfile(model, profiled, user);
|
||||
}
|
||||
|
||||
private String publicProfile(Model model, User profiled, User user) {
|
||||
if (profiled.equals(user)) return profile(model);
|
||||
|
||||
model.addAttribute("profiled", profiled);
|
||||
model.addAttribute("user", user);
|
||||
model.addAttribute("isFriend", areFriends(profiled, user));
|
||||
return "/user/public";
|
||||
}
|
||||
|
||||
private boolean areFriends(User user, User loggedIn) {
|
||||
return user!=null && user.isFriend(loggedIn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package org.neo4j.cineasts.domain;
|
||||
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.graph.annotation.RelatedToVia;
|
||||
import org.springframework.data.graph.neo4j.annotation.Indexed;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.springframework.data.graph.core.Direction.INCOMING;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@NodeEntity
|
||||
public class Movie {
|
||||
|
||||
@Indexed
|
||||
String id;
|
||||
|
||||
@Indexed(fulltext = true, indexName = "search")
|
||||
String title;
|
||||
|
||||
String description;
|
||||
|
||||
@RelatedTo(type="DIRECTED", direction = INCOMING)
|
||||
Person director;
|
||||
|
||||
@RelatedTo(elementClass = Person.class, type = "ACTS_IN", direction = INCOMING)
|
||||
Set<Person> actors;
|
||||
|
||||
@RelatedToVia(elementClass = Role.class, type = "ACTS_IN", direction = INCOMING)
|
||||
Iterable<Role> roles;
|
||||
|
||||
@RelatedToVia(elementClass = Rating.class, type = "RATED", direction = INCOMING)
|
||||
Iterable<Rating> ratings;
|
||||
private String language;
|
||||
private String imdbId;
|
||||
private String tagline;
|
||||
private Date releaseDate;
|
||||
private Integer runtime;
|
||||
private String homepage;
|
||||
private String trailer;
|
||||
private String genre;
|
||||
private String studio;
|
||||
private Integer version;
|
||||
private Date lastModified;
|
||||
private String imageUrl;
|
||||
|
||||
public Movie() {
|
||||
}
|
||||
|
||||
public Movie(String id, String title) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Collection<Person> getActors() {
|
||||
return actors;
|
||||
}
|
||||
|
||||
public Collection<Role> getRoles() {
|
||||
return IteratorUtil.asCollection(roles);
|
||||
}
|
||||
|
||||
public int getYear() {
|
||||
if (releaseDate==null) return 0;
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(releaseDate);
|
||||
return cal.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s (%s) [%s]", title, releaseDate, id);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public int getStars() {
|
||||
Iterable<Rating> allRatings = ratings;
|
||||
|
||||
if (allRatings == null) return 0;
|
||||
int stars=0, count=0;
|
||||
for (Rating rating : allRatings) {
|
||||
stars += rating.getStars();
|
||||
count++;
|
||||
}
|
||||
return count==0 ? 0 : stars / count;
|
||||
}
|
||||
|
||||
public Collection<Rating> getRatings() {
|
||||
Iterable<Rating> allRatings = ratings;
|
||||
return allRatings == null ? Collections.<Rating>emptyList() : IteratorUtil.asCollection(allRatings);
|
||||
}
|
||||
|
||||
public Person getDirector() {
|
||||
return director;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title=title;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public void setImdbId(String imdbId) {
|
||||
this.imdbId = imdbId;
|
||||
}
|
||||
|
||||
public void setTagline(String tagline) {
|
||||
this.tagline = tagline;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setReleaseDate(Date releaseDate) {
|
||||
this.releaseDate = releaseDate;
|
||||
}
|
||||
|
||||
public void setRuntime(Integer runtime) {
|
||||
this.runtime = runtime;
|
||||
}
|
||||
|
||||
public void setHomepage(String homepage) {
|
||||
this.homepage = homepage;
|
||||
}
|
||||
|
||||
public void setTrailer(String trailer) {
|
||||
this.trailer = trailer;
|
||||
}
|
||||
|
||||
public void setGenre(String genre) {
|
||||
this.genre = genre;
|
||||
}
|
||||
|
||||
public void setStudio(String studio) {
|
||||
this.studio = studio;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setLastModified(Date lastModified) {
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
|
||||
public void setImageUrl(String imageUrl) {
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public String getImdbId() {
|
||||
return imdbId;
|
||||
}
|
||||
|
||||
public String getTagline() {
|
||||
return tagline;
|
||||
}
|
||||
|
||||
public Date getReleaseDate() {
|
||||
return releaseDate;
|
||||
}
|
||||
|
||||
public Integer getRuntime() {
|
||||
return runtime;
|
||||
}
|
||||
|
||||
public String getHomepage() {
|
||||
return homepage;
|
||||
}
|
||||
|
||||
public String getTrailer() {
|
||||
return trailer;
|
||||
}
|
||||
|
||||
public String getGenre() {
|
||||
return genre;
|
||||
}
|
||||
|
||||
public String getStudio() {
|
||||
return studio;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Date getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
public String getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
public String getYoutubeId() {
|
||||
String trailerUrl = trailer;
|
||||
if (trailerUrl==null || !trailerUrl.contains("youtu")) return null;
|
||||
String[] parts = trailerUrl.split("[=/]");
|
||||
int numberOfParts = parts.length;
|
||||
return numberOfParts > 0 ? parts[numberOfParts-1] : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package org.neo4j.cineasts.domain;
|
||||
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.graph.annotation.RelatedToVia;
|
||||
import org.springframework.data.graph.neo4j.annotation.Indexed;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 12.03.11
|
||||
*/
|
||||
@NodeEntity
|
||||
public class Person {
|
||||
@Indexed
|
||||
String id;
|
||||
String name;
|
||||
private Date birthday;
|
||||
private String birthplace;
|
||||
private String biography;
|
||||
private Integer version;
|
||||
private Date lastModified;
|
||||
private String profileImageUrl;
|
||||
|
||||
public Person(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s [%s]", name, id);
|
||||
}
|
||||
|
||||
public void setBirthday(Date birthday) {
|
||||
this.birthday = birthday;
|
||||
}
|
||||
|
||||
public void setBirthplace(String birthplace) {
|
||||
this.birthplace = birthplace;
|
||||
}
|
||||
|
||||
public void setBiography(String biography) {
|
||||
this.biography = biography;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setLastModified(Date lastModified) {
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
|
||||
public Date getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
||||
public String getBirthplace() {
|
||||
return birthplace;
|
||||
}
|
||||
|
||||
public String getBiography() {
|
||||
return biography;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Date getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
public void setProfileImageUrl(String profileImageUrl) {
|
||||
this.profileImageUrl = profileImageUrl;
|
||||
}
|
||||
|
||||
public String getProfileImageUrl() {
|
||||
return profileImageUrl;
|
||||
}
|
||||
|
||||
@RelatedTo(elementClass = Movie.class, type = "DIRECTED")
|
||||
private Set<Movie> directedMovies;
|
||||
|
||||
public Set<Movie> getDirectedMovies() {
|
||||
return directedMovies;
|
||||
}
|
||||
|
||||
public void directed(Movie movie) {
|
||||
this.directedMovies.add(movie);
|
||||
}
|
||||
|
||||
@RelatedToVia(elementClass = Role.class, type = "ACTS_IN")
|
||||
Iterable<Role> roles;
|
||||
|
||||
public Iterable<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public Role playedIn(Movie movie, String roleName) {
|
||||
Role role = relateTo(movie, Role.class, "ACTS_IN");
|
||||
role.setName(roleName);
|
||||
return role;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.neo4j.cineasts.domain;
|
||||
|
||||
import org.springframework.data.graph.annotation.EndNode;
|
||||
import org.springframework.data.graph.annotation.RelationshipEntity;
|
||||
import org.springframework.data.graph.annotation.StartNode;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@RelationshipEntity
|
||||
public class Rating {
|
||||
private static final int MAX_STARS = 5;
|
||||
private static final int MIN_STARS = 0;
|
||||
|
||||
@StartNode
|
||||
User user;
|
||||
@EndNode Movie movie;
|
||||
|
||||
int stars;
|
||||
String comment;
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public Movie getMovie() {
|
||||
return movie;
|
||||
}
|
||||
|
||||
public int getStars() {
|
||||
return stars;
|
||||
}
|
||||
|
||||
public void setStars(int stars) {
|
||||
this.stars = stars;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public Rating rate(int stars, String comment) {
|
||||
if (stars>= MIN_STARS && stars <= MAX_STARS) this.stars=stars;
|
||||
if (comment!=null && !comment.isEmpty()) this.comment = comment;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.neo4j.cineasts.domain;
|
||||
|
||||
import org.springframework.data.graph.annotation.EndNode;
|
||||
import org.springframework.data.graph.annotation.RelationshipEntity;
|
||||
import org.springframework.data.graph.annotation.StartNode;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@RelationshipEntity
|
||||
public class Role {
|
||||
@EndNode Movie movie;
|
||||
@StartNode Person actor;
|
||||
|
||||
String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Movie getMovie() {
|
||||
return movie;
|
||||
}
|
||||
|
||||
public Person getActor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s acts as %s in %s", actor, name, movie);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.neo4j.cineasts.domain;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 12.03.11
|
||||
*/
|
||||
public enum Roles {
|
||||
ACTS_IN, DIRECTED
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package org.neo4j.cineasts.domain;
|
||||
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.graph.annotation.RelatedToVia;
|
||||
import org.springframework.data.graph.core.Direction;
|
||||
import org.springframework.data.graph.neo4j.annotation.Indexed;
|
||||
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
@NodeEntity
|
||||
public class User {
|
||||
private static final String SALT = "cewuiqwzie";
|
||||
public static final String FRIEND = "FRIEND";
|
||||
public static final String RATED = "RATED";
|
||||
@Indexed
|
||||
String login;
|
||||
String name;
|
||||
String password;
|
||||
String info;
|
||||
private Roles[] roles;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String login, String name, String password, Roles... roles) {
|
||||
this.login = login;
|
||||
this.name = name;
|
||||
this.password = encode(password);
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
private String encode(String password) {
|
||||
return new Md5PasswordEncoder().encodePassword(password, SALT);
|
||||
}
|
||||
|
||||
@RelatedToVia(elementClass = Rating.class, type = RATED)
|
||||
Iterable<Rating> ratings;
|
||||
|
||||
@RelatedTo(elementClass = Movie.class, type = RATED)
|
||||
Set<Movie> favorites;
|
||||
|
||||
|
||||
@RelatedTo(elementClass = User.class, type = FRIEND, direction = Direction.BOTH)
|
||||
Set<User> friends;
|
||||
|
||||
public void addFriend(User friend) {
|
||||
this.friends.add(friend);
|
||||
}
|
||||
|
||||
public Rating rate(Movie movie, int stars, String comment) {
|
||||
return relateTo(movie, Rating.class, RATED).rate(stars, comment);
|
||||
}
|
||||
|
||||
public Collection<Rating> getRatings() {
|
||||
return IteratorUtil.asCollection(ratings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s (%s)", name, login);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Set<User> getFriends() {
|
||||
return friends;
|
||||
}
|
||||
|
||||
public Roles[] getRole() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
public void updatePassword(String old, String newPass1, String newPass2) {
|
||||
if (!password.equals(encode(old))) throw new IllegalArgumentException("Existing Password invalid");
|
||||
if (!newPass1.equals(newPass2)) throw new IllegalArgumentException("New Passwords don't match");
|
||||
this.password = encode(newPass1);
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isFriend(User other) {
|
||||
return other!=null && getFriends().contains(other);
|
||||
}
|
||||
|
||||
public enum Roles implements GrantedAuthority {
|
||||
ROLE_USER, ROLE_ADMIN;
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MovieDbApiClient {
|
||||
|
||||
private final String baseUrl = "http://api.themoviedb.org/";
|
||||
private final String apiKey;
|
||||
protected final ObjectMapper mapper;
|
||||
|
||||
public MovieDbApiClient(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public Map getMovie(String id) {
|
||||
return loadJsonData(id, buildMovieUrl(id));
|
||||
}
|
||||
|
||||
private Map loadJsonData(String id, String url) {
|
||||
try {
|
||||
List value = mapper.readValue(new URL(url), List.class);
|
||||
if (value.isEmpty() || value.get(0).equals("Nothing found.")) return Collections.singletonMap("not_found",System.currentTimeMillis());
|
||||
return (Map) value.get(0);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to get data from " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildMovieUrl(String movieId) {
|
||||
return String.format("%s2.1/Movie.getInfo/en/json/%s/%s", baseUrl, apiKey, movieId);
|
||||
}
|
||||
|
||||
public Map getPerson(String id) {
|
||||
return loadJsonData(id, buildPersonUrl(id));
|
||||
}
|
||||
|
||||
private String buildPersonUrl(String personId) {
|
||||
return String.format("%s2.1/Person.getInfo/en/json/%s/%s", baseUrl, apiKey, personId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
public class MovieDbException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MovieDbException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MovieDbException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.neo4j.cineasts.domain.*;
|
||||
import org.neo4j.cineasts.service.CineastsRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class MovieDbImportService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MovieDbImportService.class);
|
||||
MovieDbJsonMapper movieDbJsonMapper = new MovieDbJsonMapper();
|
||||
|
||||
@Autowired
|
||||
CineastsRepository moviesRepository;
|
||||
|
||||
@Autowired
|
||||
MovieDbApiClient client;
|
||||
|
||||
@Autowired
|
||||
MovieDbLocalStorage localStorage;
|
||||
|
||||
@Transactional
|
||||
public Map<Integer, String> importMovies(Map<Integer, Integer> ranges) {
|
||||
final Map<Integer,String> movies=new LinkedHashMap<Integer, String>();
|
||||
for (Map.Entry<Integer, Integer> entry : ranges.entrySet()) {
|
||||
for (int id = entry.getKey(); id <= entry.getValue(); id++) {
|
||||
String result = importMovieFailsafe(id);
|
||||
movies.put(id, result);
|
||||
}
|
||||
}
|
||||
return movies;
|
||||
}
|
||||
|
||||
private String importMovieFailsafe(Integer id) {
|
||||
try {
|
||||
Movie movie = doImportMovie(String.valueOf(id));
|
||||
return movie.getTitle();
|
||||
} catch (Exception e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Movie importMovie(String movieId) {
|
||||
return doImportMovie(movieId);
|
||||
}
|
||||
|
||||
private Movie doImportMovie(String movieId) {
|
||||
logger.debug("Importing movie " + movieId);
|
||||
|
||||
Movie movie = moviesRepository.getMovie(movieId);
|
||||
if (movie == null) { // Not found: Create fresh
|
||||
movie = new Movie(movieId,null);
|
||||
}
|
||||
|
||||
Map data = loadMovieData(movieId);
|
||||
if (data.containsKey("not_found")) throw new RuntimeException("Data for Movie "+movieId+" not found.");
|
||||
movieDbJsonMapper.mapToMovie(data, movie);
|
||||
movie.persist();
|
||||
relatePersonsToMovie(movie, data);
|
||||
return movie;
|
||||
}
|
||||
|
||||
private Map loadMovieData(String movieId) {
|
||||
if (localStorage.hasMovie(movieId)) {
|
||||
return localStorage.loadMovie(movieId);
|
||||
}
|
||||
|
||||
Map data = client.getMovie(movieId);
|
||||
localStorage.storeMovie(movieId, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private void relatePersonsToMovie(Movie movie, Map data) {
|
||||
Collection<Map> cast = (Collection<Map>) data.get("cast");
|
||||
for (Map entry : cast) {
|
||||
String id = "" + entry.get("id");
|
||||
String jobName = (String) entry.get("job");
|
||||
Roles job = movieDbJsonMapper.mapToRole(jobName);
|
||||
if (job==null) {
|
||||
if (logger.isInfoEnabled()) logger.info("Could not add person with job "+jobName+" "+entry);
|
||||
continue;
|
||||
}
|
||||
Person person = doImportPerson(id);
|
||||
switch (job) {
|
||||
case DIRECTED:
|
||||
person.directed(movie);
|
||||
break;
|
||||
case ACTS_IN:
|
||||
person.playedIn(movie, (String) entry.get("character"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Person importPerson(String personId) {
|
||||
return doImportPerson(personId);
|
||||
}
|
||||
|
||||
private Person doImportPerson(String personId) {
|
||||
logger.debug("Importing person " + personId);
|
||||
Person person = moviesRepository.getPerson(personId);
|
||||
if (person!=null) return person;
|
||||
Map data = loadPersonData(personId);
|
||||
if (data.containsKey("not_found")) throw new RuntimeException("Data for Person "+personId+" not found.");
|
||||
Person newPerson=new Person(personId,null);
|
||||
movieDbJsonMapper.mapToPerson(data, newPerson);
|
||||
return newPerson.persist();
|
||||
}
|
||||
|
||||
private Map loadPersonData(String personId) {
|
||||
if (localStorage.hasPerson(personId)) {
|
||||
return localStorage.loadPerson(personId);
|
||||
}
|
||||
Map data = client.getPerson(personId);
|
||||
localStorage.storePerson(personId, data);
|
||||
return localStorage.loadPerson(personId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.neo4j.cineasts.domain.Person;
|
||||
import org.neo4j.cineasts.domain.Roles;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class MovieDbJsonMapper {
|
||||
|
||||
public void mapToMovie(Map data, Movie movie) {
|
||||
try {
|
||||
movie.setTitle((String) data.get("name"));
|
||||
movie.setLanguage((String) data.get("language"));
|
||||
movie.setImdbId((String) data.get("imdb_id"));
|
||||
movie.setTagline((String) data.get("tagline"));
|
||||
movie.setDescription(limit((String) data.get("overview"), 500));
|
||||
movie.setReleaseDate(toDate(data, "released", "yyyy-MM-dd"));
|
||||
movie.setRuntime((Integer) data.get("runtime"));
|
||||
movie.setHomepage((String) data.get("homepage"));
|
||||
movie.setTrailer((String) data.get("trailer"));
|
||||
movie.setGenre(extractFirst(data, "genres", "name"));
|
||||
movie.setStudio(extractFirst(data,"studios", "name"));
|
||||
movie.setVersion((Integer)data.get("version"));
|
||||
movie.setLastModified(toDate(data,"last_modified_at","yyyy-MM-dd HH:mm:ss"));
|
||||
movie.setImageUrl(selectImageUrl((List<Map>) data.get("posters"), "poster", "mid"));
|
||||
} catch (Exception e) {
|
||||
throw new MovieDbException("Failed to map json for movie", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String selectImageUrl(List<Map> data, final String type, final String size) {
|
||||
if (data==null) return null;
|
||||
for (Map entry : data) {
|
||||
Map image = (Map) entry.get("image");
|
||||
if (image.get("type").equals(type) && image.get("size").equals(size)) return (String) image.get("url");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private String extractFirst(Map data, String field, String property) {
|
||||
List<Map> inner = (List<Map>) data.get(field);
|
||||
if (inner == null || inner.isEmpty()) return null;
|
||||
return (String) inner.get(0).get(property);
|
||||
}
|
||||
|
||||
private Date toDate(Map data, String field, final String pattern) throws ParseException {
|
||||
String dateString = (String) data.get(field);
|
||||
if (dateString == null || dateString.isEmpty()) return null;
|
||||
return new SimpleDateFormat(pattern).parse(dateString);
|
||||
}
|
||||
|
||||
/*
|
||||
[{"popularity":3,
|
||||
"translated":true,
|
||||
"adult":false,
|
||||
"language":"en",
|
||||
"original_name":"[Rec]",
|
||||
"name":"[Rec]",
|
||||
"alternative_name":"[REC]",
|
||||
"movie_type":"movie",
|
||||
"id":8329,
|
||||
"imdb_id":"tt1038988",
|
||||
"url":"http://www.themoviedb.org/movie/8329",
|
||||
"votes":11,
|
||||
"rating":7.2,
|
||||
"status":"Released",
|
||||
"tagline":"One Witness. One Camera",
|
||||
"certification":"R",
|
||||
"overview":"\"REC\" turns on a young TV reporter and her cameraman who cover the night shift at the local fire station. Receiving a call from an old lady trapped in her house,
|
||||
they reach her building to hear horrifying screams -- which begin a long nightmare and a uniquely dramatic TV report.",
|
||||
"keywords":["terror",
|
||||
"lebende leichen",
|
||||
"obsession",
|
||||
"camcorder",
|
||||
"firemen",
|
||||
"reality tv ",
|
||||
"bite",
|
||||
"cinematographer",
|
||||
"attempt to escape",
|
||||
"virus",
|
||||
"lodger",
|
||||
"live-reportage",
|
||||
"schwerverletzt"],
|
||||
"released":"2007-08-29",
|
||||
"runtime":78,
|
||||
"budget":0,
|
||||
"revenue":0,
|
||||
"homepage":"http://www.3l-filmverleih.de/rec",
|
||||
"trailer":"http://www.youtube.com/watch?v=YQUkX_XowqI",
|
||||
"genres":[{"type":"genre",
|
||||
"url":"http://themoviedb.org/genre/horror",
|
||||
"name":"Horror",
|
||||
"id":27}],
|
||||
"studios":[{"url":"http://www.themoviedb.org/company/2270",
|
||||
"name":"Filmax Group",
|
||||
"id":2270}],
|
||||
"languages_spoken":[{"code":"es",
|
||||
"name":"Spanish",
|
||||
"native_name":"Espa\u00f1ol"}],
|
||||
"countries":[{"code":"ES",
|
||||
"name":"Spain",
|
||||
"url":"http://www.themoviedb.org/country/es"}],
|
||||
"posters":[{"image":{"type":"poster",
|
||||
"size":"original",
|
||||
"height":1000,
|
||||
"width":706,
|
||||
"url":"http://cf1.imgobject.com/posters/3a0/4cc8df415e73d650240003a0/rec-original.jpg",
|
||||
"id":"4cc8df415e73d650240003a0"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"mid",
|
||||
"height":708,
|
||||
"width":500,
|
||||
"url":"http://cf1.imgobject.com/posters/3a0/4cc8df415e73d650240003a0/rec-mid.jpg",
|
||||
"id":"4cc8df415e73d650240003a0"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"cover",
|
||||
"height":262,
|
||||
"width":185,
|
||||
"url":"http://cf1.imgobject.com/posters/3a0/4cc8df415e73d650240003a0/rec-cover.jpg",
|
||||
"id":"4cc8df415e73d650240003a0"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"thumb",
|
||||
"height":130,
|
||||
"width":92,
|
||||
"url":"http://cf1.imgobject.com/posters/3a0/4cc8df415e73d650240003a0/rec-thumb.jpg",
|
||||
"id":"4cc8df415e73d650240003a0"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"original",
|
||||
"height":1000,
|
||||
"width":711,
|
||||
"url":"http://cf1.imgobject.com/posters/1e7/4bc92117017a3c57fe00d1e7/rec-original.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1e7"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"mid",
|
||||
"height":703,
|
||||
"width":500,
|
||||
"url":"http://cf1.imgobject.com/posters/1e7/4bc92117017a3c57fe00d1e7/rec-mid.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1e7"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"cover",
|
||||
"height":260,
|
||||
"width":185,
|
||||
"url":"http://cf1.imgobject.com/posters/1e7/4bc92117017a3c57fe00d1e7/rec-cover.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1e7"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"thumb",
|
||||
"height":129,
|
||||
"width":92,
|
||||
"url":"http://cf1.imgobject.com/posters/1e7/4bc92117017a3c57fe00d1e7/rec-thumb.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1e7"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"original",
|
||||
"height":1500,
|
||||
"width":1000,
|
||||
"url":"http://cf1.imgobject.com/posters/1ec/4bc92118017a3c57fe00d1ec/rec-original.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1ec"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"mid",
|
||||
"height":750,
|
||||
"width":500,
|
||||
"url":"http://cf1.imgobject.com/posters/1ec/4bc92118017a3c57fe00d1ec/rec-mid.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1ec"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"cover",
|
||||
"height":278,
|
||||
"width":185,
|
||||
"url":"http://cf1.imgobject.com/posters/1ec/4bc92118017a3c57fe00d1ec/rec-cover.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1ec"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"thumb",
|
||||
"height":138,
|
||||
"width":92,
|
||||
"url":"http://cf1.imgobject.com/posters/1ec/4bc92118017a3c57fe00d1ec/rec-thumb.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1ec"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"original",
|
||||
"height":1429,
|
||||
"width":1000,
|
||||
"url":"http://cf1.imgobject.com/posters/1f1/4bc92118017a3c57fe00d1f1/rec-original.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f1"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"mid",
|
||||
"height":715,
|
||||
"width":500,
|
||||
"url":"http://cf1.imgobject.com/posters/1f1/4bc92118017a3c57fe00d1f1/rec-mid.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f1"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"cover",
|
||||
"height":265,
|
||||
"width":185,
|
||||
"url":"http://cf1.imgobject.com/posters/1f1/4bc92118017a3c57fe00d1f1/rec-cover.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f1"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"thumb",
|
||||
"height":132,
|
||||
"width":92,
|
||||
"url":"http://cf1.imgobject.com/posters/1f1/4bc92118017a3c57fe00d1f1/rec-thumb.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f1"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"original",
|
||||
"height":1125,
|
||||
"width":800,
|
||||
"url":"http://cf1.imgobject.com/posters/1f6/4bc92118017a3c57fe00d1f6/rec-original.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f6"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"mid",
|
||||
"height":703,
|
||||
"width":500,
|
||||
"url":"http://cf1.imgobject.com/posters/1f6/4bc92118017a3c57fe00d1f6/rec-mid.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f6"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"cover",
|
||||
"height":260,
|
||||
"width":185,
|
||||
"url":"http://cf1.imgobject.com/posters/1f6/4bc92118017a3c57fe00d1f6/rec-cover.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f6"}},
|
||||
{"image":{"type":"poster",
|
||||
"size":"thumb",
|
||||
"height":129,
|
||||
"width":92,
|
||||
"url":"http://cf1.imgobject.com/posters/1f6/4bc92118017a3c57fe00d1f6/rec-thumb.jpg",
|
||||
"id":"4bc92118017a3c57fe00d1f6"}}],
|
||||
"backdrops":[{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1df/4bc92117017a3c57fe00d1df/rec-original.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1df"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1df/4bc92117017a3c57fe00d1df/rec-poster.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1df"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1df/4bc92117017a3c57fe00d1df/rec-thumb.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1df"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1d7/4bc92113017a3c57fe00d1d7/rec-original.jpg",
|
||||
"id":"4bc92113017a3c57fe00d1d7"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1d7/4bc92113017a3c57fe00d1d7/rec-poster.jpg",
|
||||
"id":"4bc92113017a3c57fe00d1d7"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1d7/4bc92113017a3c57fe00d1d7/rec-thumb.jpg",
|
||||
"id":"4bc92113017a3c57fe00d1d7"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1e3/4bc92117017a3c57fe00d1e3/rec-original.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1e3"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1e3/4bc92117017a3c57fe00d1e3/rec-poster.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1e3"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1e3/4bc92117017a3c57fe00d1e3/rec-thumb.jpg",
|
||||
"id":"4bc92117017a3c57fe00d1e3"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1d3/4bc92113017a3c57fe00d1d3/rec-original.jpg",
|
||||
"id":"4bc92113017a3c57fe00d1d3"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1d3/4bc92113017a3c57fe00d1d3/rec-poster.jpg",
|
||||
"id":"4bc92113017a3c57fe00d1d3"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1d3/4bc92113017a3c57fe00d1d3/rec-thumb.jpg",
|
||||
"id":"4bc92113017a3c57fe00d1d3"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1db/4bc92114017a3c57fe00d1db/rec-original.jpg",
|
||||
"id":"4bc92114017a3c57fe00d1db"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1db/4bc92114017a3c57fe00d1db/rec-poster.jpg",
|
||||
"id":"4bc92114017a3c57fe00d1db"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/1db/4bc92114017a3c57fe00d1db/rec-thumb.jpg",
|
||||
"id":"4bc92114017a3c57fe00d1db"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/6f4/4d0e39c25e73d637100016f4/rec-original.jpg",
|
||||
"id":"4d0e39c25e73d637100016f4"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/6f4/4d0e39c25e73d637100016f4/rec-poster.jpg",
|
||||
"id":"4d0e39c25e73d637100016f4"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/6f4/4d0e39c25e73d637100016f4/rec-thumb.jpg",
|
||||
"id":"4d0e39c25e73d637100016f4"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/567/4d0e39dd5e73d6370a001567/rec-original.jpg",
|
||||
"id":"4d0e39dd5e73d6370a001567"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/567/4d0e39dd5e73d6370a001567/rec-poster.jpg",
|
||||
"id":"4d0e39dd5e73d6370a001567"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/567/4d0e39dd5e73d6370a001567/rec-thumb.jpg",
|
||||
"id":"4d0e39dd5e73d6370a001567"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/58a/4d0e39fb5e73d6371c00158a/rec-original.jpg",
|
||||
"id":"4d0e39fb5e73d6371c00158a"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/58a/4d0e39fb5e73d6371c00158a/rec-poster.jpg",
|
||||
"id":"4d0e39fb5e73d6371c00158a"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/58a/4d0e39fb5e73d6371c00158a/rec-thumb.jpg",
|
||||
"id":"4d0e39fb5e73d6371c00158a"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/5f4/4d0e3a185e73d6370e0015f4/rec-original.jpg",
|
||||
"id":"4d0e3a185e73d6370e0015f4"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/5f4/4d0e3a185e73d6370e0015f4/rec-poster.jpg",
|
||||
"id":"4d0e3a185e73d6370e0015f4"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/5f4/4d0e3a185e73d6370e0015f4/rec-thumb.jpg",
|
||||
"id":"4d0e3a185e73d6370e0015f4"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"original",
|
||||
"height":1080,
|
||||
"width":1920,
|
||||
"url":"http://cf1.imgobject.com/backdrops/67f/4d0e3a395e73d6371600167f/rec-original.jpg",
|
||||
"id":"4d0e3a395e73d6371600167f"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"poster",
|
||||
"height":439,
|
||||
"width":780,
|
||||
"url":"http://cf1.imgobject.com/backdrops/67f/4d0e3a395e73d6371600167f/rec-poster.jpg",
|
||||
"id":"4d0e3a395e73d6371600167f"}},
|
||||
{"image":{"type":"backdrop",
|
||||
"size":"thumb",
|
||||
"height":169,
|
||||
"width":300,
|
||||
"url":"http://cf1.imgobject.com/backdrops/67f/4d0e3a395e73d6371600167f/rec-thumb.jpg",
|
||||
"id":"4d0e3a395e73d6371600167f"}}],
|
||||
"cast":[{"name":"Manuela Velasco",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Angela Vidal",
|
||||
"id":34793,
|
||||
"order":0,
|
||||
"cast_id":1,
|
||||
"url":"http://www.themoviedb.org/person/34793",
|
||||
"profile":"http://cf1.imgobject.com/profiles/390/4c0157fa017a3c702d001390/manuela-velasco-thumb.jpg"},
|
||||
{"name":"Carlos Lasarte",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"C\u00e9sar",
|
||||
"id":54519,
|
||||
"order":2,
|
||||
"cast_id":3,
|
||||
"url":"http://www.themoviedb.org/person/54519",
|
||||
"profile":"http://cf1.imgobject.com/profiles/ba6/4d27aa297b9aa134d3001ba6/carlos-lasarte-thumb.jpg"},
|
||||
{"name":"Pablo Rosso",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Marcos",
|
||||
"id":54520,
|
||||
"order":3,
|
||||
"cast_id":4,
|
||||
"url":"http://www.themoviedb.org/person/54520",
|
||||
"profile":""},
|
||||
{"name":"David Vert",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Alex",
|
||||
"id":54521,
|
||||
"order":4,
|
||||
"cast_id":5,
|
||||
"url":"http://www.themoviedb.org/person/54521",
|
||||
"profile":"http://cf1.imgobject.com/profiles/c7e/4d27aad57b9aa134cb001c7e/david-vert-thumb.jpg"},
|
||||
{"name":"Vicente Gil",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Polizist",
|
||||
"id":54522,
|
||||
"order":5,
|
||||
"cast_id":6,
|
||||
"url":"http://www.themoviedb.org/person/54522",
|
||||
"profile":"http://cf1.imgobject.com/profiles/c86/4d27ab237b9aa134cb001c86/vicente-gil-thumb.jpg"},
|
||||
{"name":"Martha Carbonell",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Frau Izquierdo",
|
||||
"id":54523,
|
||||
"order":6,
|
||||
"cast_id":7,
|
||||
"url":"http://www.themoviedb.org/person/54523",
|
||||
"profile":"http://cf1.imgobject.com/profiles/c90/4d27ab777b9aa134cb001c90/martha-carbonell-thumb.jpg"},
|
||||
{"name":"Carlos Vicente",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Guillem",
|
||||
"id":54524,
|
||||
"order":7,
|
||||
"cast_id":8,
|
||||
"url":"http://www.themoviedb.org/person/54524",
|
||||
"profile":"http://cf1.imgobject.com/profiles/b0f/4d27abcc7b9aa134cf001b0f/carlos-vicente-thumb.jpg"},
|
||||
{"name":"Ferran Terraza",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Manu",
|
||||
"id":54532,
|
||||
"order":8,
|
||||
"cast_id":22,
|
||||
"url":"http://www.themoviedb.org/person/54532",
|
||||
"profile":"http://cf1.imgobject.com/profiles/129/4c4bfbab7b9aa1237f000129/ferran-terraza-thumb.jpg"},
|
||||
{"name":"Jorge Yamam",
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Sergio",
|
||||
"id":147878,
|
||||
"order":9,
|
||||
"cast_id":23,
|
||||
"url":"http://www.themoviedb.org/person/147878",
|
||||
"profile":"http://cf1.imgobject.com/profiles/cbb/4d27ac677b9aa134dc001cbb/jorge-yamam-thumb.jpg"},
|
||||
{"name":"Jaume Balaguer\u00f3",
|
||||
"job":"Director",
|
||||
"department":"Directing",
|
||||
"character":"",
|
||||
"id":54525,
|
||||
"order":0,
|
||||
"cast_id":9,
|
||||
"url":"http://www.themoviedb.org/person/54525",
|
||||
"profile":""},
|
||||
{"name":"Paco Plaza",
|
||||
"job":"Director",
|
||||
"department":"Directing",
|
||||
"character":"",
|
||||
"id":54526,
|
||||
"order":0,
|
||||
"cast_id":10,
|
||||
"url":"http://www.themoviedb.org/person/54526",
|
||||
"profile":""},
|
||||
{"name":"Jaume Balaguer\u00f3",
|
||||
"job":"Screenplay",
|
||||
"department":"Writing",
|
||||
"character":"",
|
||||
"id":54525,
|
||||
"order":0,
|
||||
"cast_id":11,
|
||||
"url":"http://www.themoviedb.org/person/54525",
|
||||
"profile":""},
|
||||
{"name":"Paco Plaza",
|
||||
"job":"Screenplay",
|
||||
"department":"Writing",
|
||||
"character":"",
|
||||
"id":54526,
|
||||
"order":0,
|
||||
"cast_id":12,
|
||||
"url":"http://www.themoviedb.org/person/54526",
|
||||
"profile":""},
|
||||
{"name":"Luis Berdejo",
|
||||
"job":"Screenplay",
|
||||
"department":"Writing",
|
||||
"character":"",
|
||||
"id":54527,
|
||||
"order":0,
|
||||
"cast_id":13,
|
||||
"url":"http://www.themoviedb.org/person/54527",
|
||||
"profile":""},
|
||||
{"name":"Julio Fern\u00e1ndez",
|
||||
"job":"Producer",
|
||||
"department":"Production",
|
||||
"character":"",
|
||||
"id":17083,
|
||||
"order":0,
|
||||
"cast_id":14,
|
||||
"url":"http://www.themoviedb.org/person/17083",
|
||||
"profile":""},
|
||||
{"name":"Carlos Fern\u00e1ndez",
|
||||
"job":"Executive Producer",
|
||||
"department":"Production",
|
||||
"character":"",
|
||||
"id":37950,
|
||||
"order":0,
|
||||
"cast_id":15,
|
||||
"url":"http://www.themoviedb.org/person/37950",
|
||||
"profile":""},
|
||||
{"name":"Alberto Marini",
|
||||
"job":"Producer",
|
||||
"department":"Production",
|
||||
"character":"",
|
||||
"id":54528,
|
||||
"order":0,
|
||||
"cast_id":16,
|
||||
"url":"http://www.themoviedb.org/person/54528",
|
||||
"profile":""},
|
||||
{"name":"Pablo Rosso",
|
||||
"job":"Director of Photography",
|
||||
"department":"Camera",
|
||||
"character":"",
|
||||
"id":54520,
|
||||
"order":0,
|
||||
"cast_id":17,
|
||||
"url":"http://www.themoviedb.org/person/54520",
|
||||
"profile":""},
|
||||
{"name":"Gemma Fauria",
|
||||
"job":"Set Designer",
|
||||
"department":"Art",
|
||||
"character":"",
|
||||
"id":54529,
|
||||
"order":0,
|
||||
"cast_id":18,
|
||||
"url":"http://www.themoviedb.org/person/54529",
|
||||
"profile":""},
|
||||
{"name":"David Gallart",
|
||||
"job":"Editor",
|
||||
"department":"Editing",
|
||||
"character":"",
|
||||
"id":28500,
|
||||
"order":0,
|
||||
"cast_id":19,
|
||||
"url":"http://www.themoviedb.org/person/28500",
|
||||
"profile":""},
|
||||
{"name":"Xavier Mas",
|
||||
"job":"Sound Designer",
|
||||
"department":"Sound",
|
||||
"character":"",
|
||||
"id":54530,
|
||||
"order":0,
|
||||
"cast_id":20,
|
||||
"url":"http://www.themoviedb.org/person/54530",
|
||||
"profile":""},
|
||||
{"name":"Gl\u00f2ria Viguer",
|
||||
"job":"Costume Design",
|
||||
"department":"Costume \u0026 Make-Up",
|
||||
"character":"",
|
||||
"id":54531,
|
||||
"order":0,
|
||||
"cast_id":21,
|
||||
"url":"http://www.themoviedb.org/person/54531",
|
||||
"profile":""}],
|
||||
"version":150,
|
||||
"last_modified_at":"2011-02-20 23:16:57"}]
|
||||
*/
|
||||
|
||||
public void mapToPerson(Map data, Person person) {
|
||||
try {
|
||||
person.setName((String) data.get("name"));
|
||||
person.setBirthday(toDate(data, "birthday", "yyyy-MM-dd"));
|
||||
person.setBirthplace((String) data.get("birthplace"));
|
||||
String biography = (String) data.get("biography");
|
||||
person.setBiography(limit(biography,500));
|
||||
person.setVersion((Integer) data.get("version"));
|
||||
person.setProfileImageUrl(selectImageUrl((List<Map>) data.get("profile"), "profile", "profile"));
|
||||
person.setLastModified(toDate(data,"last_modified_at","yyyy-MM-dd HH:mm:ss"));
|
||||
} catch (Exception e) {
|
||||
throw new MovieDbException("Failed to map json for person", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String limit(String text, int limit) {
|
||||
if (text==null || text.length() < limit) return text;
|
||||
return text.substring(0,limit);
|
||||
}
|
||||
|
||||
/*
|
||||
[{"popularity":3,
|
||||
"name":"Glenn Strange",
|
||||
"known_as":[{"name":"George Glenn Strange"},
|
||||
{"name":"Glen Strange"},
|
||||
{"name":"Glen 'Peewee' Strange"},
|
||||
{"name":"Peewee Strange"},
|
||||
{"name":"'Peewee' Strange"}],
|
||||
"id":30112,
|
||||
"biography":"",
|
||||
"known_movies":4,
|
||||
"birthday":"1899-08-16",
|
||||
"birthplace":"Weed,
|
||||
New Mexico,
|
||||
USA",
|
||||
"url":"http://www.themoviedb.org/person/30112",
|
||||
"filmography":[{"name":"Bud Abbott Lou Costello Meet Frankenstein",
|
||||
"id":3073,
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"The Frankenstein Monster",
|
||||
"cast_id":23,
|
||||
"url":"http://www.themoviedb.org/movie/3073",
|
||||
"poster":"http://cf1.imgobject.com/posters/4ca/4bc9185d017a3c57fe0094ca/bud-abbott-lou-costello-meet-frankenstein-cover.jpg",
|
||||
"adult":false,
|
||||
"release":"1948-06-15"},
|
||||
{"name":"Arizona Days (1937)",
|
||||
"id":22367,
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Henchman Pete ",
|
||||
"cast_id":12,
|
||||
"url":"http://www.themoviedb.org/movie/22367",
|
||||
"poster":"",
|
||||
"adult":false,
|
||||
"release":"1937-01-30"},
|
||||
{"name":"House of Dracula",
|
||||
"id":30793,
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"The Frankenstein Monster",
|
||||
"cast_id":3,
|
||||
"url":"http://www.themoviedb.org/movie/30793",
|
||||
"poster":"http://cf1.imgobject.com/posters/7cf/4bc97660017a3c57fe0367cf/house-of-dracula-cover.jpg",
|
||||
"adult":false,
|
||||
"release":"1945-01-01"},
|
||||
{"name":"The Fargo Kid",
|
||||
"id":38704,
|
||||
"job":"Actor",
|
||||
"department":"Actors",
|
||||
"character":"Sheriff Dave",
|
||||
"cast_id":24,
|
||||
"url":"http://www.themoviedb.org/movie/38704",
|
||||
"poster":"http://cf1.imgobject.com/posters/04e/4d74018a5e73d6021d00004e/the-fargo-kid-cover.jpg",
|
||||
"adult":false,
|
||||
"release":"1940-12-06"}],
|
||||
"profile":[],
|
||||
"version":19,
|
||||
"last_modified_at":"2011-03-07 13:02:35"}]
|
||||
*/
|
||||
|
||||
|
||||
public Roles mapToRole(String roleString) {
|
||||
if (roleString.equals("Actor")) {
|
||||
return Roles.ACTS_IN;
|
||||
}
|
||||
if (roleString.equals("Director")) {
|
||||
return Roles.DIRECTED;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MovieDbLocalStorage {
|
||||
|
||||
private String storagePath;
|
||||
protected ObjectMapper mapper;
|
||||
|
||||
public MovieDbLocalStorage(String storagePath) {
|
||||
this.storagePath = storagePath;
|
||||
ensureStorageDirectoryExists();
|
||||
mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
private void ensureStorageDirectoryExists() {
|
||||
File storageDirectory = new File(storagePath);
|
||||
if (!storageDirectory.isDirectory()) {
|
||||
if (!storageDirectory.mkdirs()) {
|
||||
throw new RuntimeException("Failed to create storage dir");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasMovie(String movieId) {
|
||||
return fileForMovie(movieId).exists();
|
||||
}
|
||||
|
||||
private File fileForMovie(String movieId) {
|
||||
return new File(storagePath, String.format("movie_%s.json", movieId));
|
||||
}
|
||||
|
||||
public Map loadMovie(String movieId) {
|
||||
File storageFile = fileForMovie(movieId);
|
||||
return loadJsonValue(storageFile);
|
||||
}
|
||||
|
||||
private Map loadJsonValue(File storageFile) {
|
||||
try {
|
||||
final Object value = mapper.readValue(storageFile, Object.class);
|
||||
if (value instanceof List) {
|
||||
List list = (List) value;
|
||||
if (list.isEmpty() || list.get(0).equals("Nothing found.")) return Collections.singletonMap("not_found", System.currentTimeMillis());
|
||||
return asMap(list.get(0));
|
||||
}
|
||||
return asMap(value);
|
||||
} catch (Exception e) {
|
||||
throw new MovieDbException("Failed to load JSON from storage for file " + storageFile.getPath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Map asMap(Object value) {
|
||||
if (value instanceof Map) {
|
||||
return (Map) value;
|
||||
}
|
||||
final String typeInformation = value == null ? "null" : value.getClass().getSimpleName();
|
||||
throw new MovieDbException("Wrong movie data format, expected Map/JSON-Object but was "+ typeInformation);
|
||||
}
|
||||
|
||||
public void storeMovie(String movieId, Object movieData) {
|
||||
File storageFile = fileForMovie(movieId);
|
||||
storeJsonValue(movieData, storageFile);
|
||||
}
|
||||
|
||||
private void storeJsonValue(Object jsonData, File storageFile) {
|
||||
try {
|
||||
mapper.writeValue(storageFile,jsonData);
|
||||
} catch (Exception e) {
|
||||
throw new MovieDbException("Failed to store JSON to storage for file " + storageFile.getPath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPerson(String personId) {
|
||||
return fileForPerson(personId).exists();
|
||||
}
|
||||
|
||||
private File fileForPerson(String personId) {
|
||||
return new File(storagePath, String.format("person_%s.json", personId));
|
||||
}
|
||||
|
||||
public Map loadPerson(String personId) {
|
||||
File storageFile = fileForPerson(personId);
|
||||
return loadJsonValue(storageFile);
|
||||
}
|
||||
|
||||
public void storePerson(String personId, Object personJson) {
|
||||
File storageFile = fileForPerson(personId);
|
||||
storeJsonValue(personJson, storageFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.neo4j.cineasts.repository;
|
||||
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.springframework.data.graph.neo4j.repository.GraphRepository;
|
||||
import org.springframework.data.graph.neo4j.repository.NamedIndexRepository;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 02.04.11
|
||||
*/
|
||||
public interface MovieRepository extends GraphRepository<Movie>, NamedIndexRepository<Movie> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.neo4j.cineasts.repository;
|
||||
|
||||
import org.neo4j.cineasts.domain.Person;
|
||||
import org.springframework.data.graph.neo4j.repository.GraphRepository;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 02.04.11
|
||||
*/
|
||||
public interface PersonRepository extends GraphRepository<Person> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.neo4j.cineasts.repository;
|
||||
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.springframework.data.graph.neo4j.repository.GraphRepository;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 02.04.11
|
||||
*/
|
||||
public interface UserRepository extends GraphRepository<User> {
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.neo4j.cineasts.domain.Person;
|
||||
import org.neo4j.cineasts.domain.Rating;
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.neo4j.cineasts.repository.MovieRepository;
|
||||
import org.neo4j.cineasts.repository.PersonRepository;
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public class CineastsRepository {
|
||||
|
||||
@Autowired private PersonRepository personRepository;
|
||||
@Autowired private MovieRepository movieRepository;
|
||||
|
||||
|
||||
public Movie getMovie(String id) {
|
||||
return movieRepository.findByPropertyValue("id", id);
|
||||
}
|
||||
|
||||
public List<Movie> findMovies(String query, int max) {
|
||||
if (query.isEmpty()) return Collections.emptyList();
|
||||
if (max < 1 || max > 1000) max = 100;
|
||||
|
||||
ClosableIterable<Movie> searchResult = movieRepository.findAllByQuery("search", "title", query);
|
||||
List<Movie> result=new ArrayList<Movie>(max);
|
||||
for (Movie movie : searchResult) {
|
||||
result.add(movie);
|
||||
if (--max == 0) break;
|
||||
}
|
||||
searchResult.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Person getPerson(String id) {
|
||||
return personRepository.findByPropertyValue("id",id);
|
||||
}
|
||||
|
||||
public Rating rateMovie(Movie movie, User user, int stars, String comment) {
|
||||
if (user == null || movie==null) return null;
|
||||
return user.rate(movie, stars,comment);
|
||||
}
|
||||
|
||||
public Map<Movie,?> recommendMovies(User user, final int ratingDistance) {
|
||||
final FriendsMovieRecommendations movieRecommendations = new FriendsMovieRecommendations(ratingDistance);
|
||||
return movieRecommendations.getRecommendationsFor(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 07.03.11
|
||||
*/
|
||||
public class CineastsUserDetails implements UserDetails {
|
||||
private final User user;
|
||||
|
||||
public CineastsUserDetails(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<GrantedAuthority> getAuthorities() {
|
||||
User.Roles[] roles = user.getRole();
|
||||
if (roles ==null) return Collections.emptyList();
|
||||
return Arrays.<GrantedAuthority>asList(roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return user.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return user.getLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.neo4j.cineasts.repository.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 06.03.11
|
||||
*/
|
||||
@Service
|
||||
public class CineastsUserDetailsService implements UserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException, DataAccessException {
|
||||
final User user = findUser(login);
|
||||
if (user==null) throw new UsernameNotFoundException("Username not found",login);
|
||||
return new CineastsUserDetails(user);
|
||||
}
|
||||
|
||||
public User findUser(String login) {
|
||||
return userRepository.findByPropertyValue("login",login);
|
||||
}
|
||||
|
||||
|
||||
public User getUserFromSession() {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
Authentication authentication = context.getAuthentication();
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (principal instanceof CineastsUserDetails) {
|
||||
CineastsUserDetails userDetails = (CineastsUserDetails) principal;
|
||||
return userDetails.getUser();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User register(String login, String name, String password) {
|
||||
User found = findUser(login);
|
||||
if (found!=null) throw new RuntimeException("Login already taken: "+login);
|
||||
if (name==null || name.isEmpty()) throw new RuntimeException("No name provided.");
|
||||
if (password==null || password.isEmpty()) throw new RuntimeException("No password provided.");
|
||||
User user=userRepository.save(new User(login,name,password,User.Roles.ROLE_USER));
|
||||
setUserInSession(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
private void setUserInSession(User user) {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
CineastsUserDetails userDetails = new CineastsUserDetails(user);
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, user.getPassword(),userDetails.getAuthorities());
|
||||
context.setAuthentication(authentication);
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void addFriend(String login) {
|
||||
User friend = findUser(login);
|
||||
User user = getUserFromSession();
|
||||
if (!user.equals(friend)) {
|
||||
user.addFriend(friend);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.neo4j.kernel.AbstractGraphDatabase;
|
||||
import org.neo4j.cineasts.domain.*;
|
||||
import org.neo4j.cineasts.movieimport.MovieDbImportService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@Service
|
||||
public class DatabasePopulator {
|
||||
|
||||
@Autowired
|
||||
GraphDatabaseContext ctx;
|
||||
@Autowired
|
||||
CineastsRepository repository;
|
||||
|
||||
@Autowired
|
||||
MovieDbImportService importService;
|
||||
private final static Logger log = LoggerFactory.getLogger(DatabasePopulator.class);
|
||||
|
||||
@Transactional
|
||||
public List<Movie> populateDatabase() {
|
||||
User me = new User("micha", "Micha", "password", User.Roles.ROLE_ADMIN,User.Roles.ROLE_USER).persist();
|
||||
User ollie = new User("ollie", "Olliver", "password",User.Roles.ROLE_USER).persist();
|
||||
me.addFriend(ollie);
|
||||
List<Integer> ids = asList(19995 , 194, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 13, 20526, 11, 1893, 1892, 1894, 168, 193, 200, 157, 152, 201, 154, 12155, 58, 285, 118, 22, 392, 5255, 568, 9800, 497, 101, 120, 121, 122);
|
||||
List<Movie> result=new ArrayList<Movie>(ids.size());
|
||||
for (Integer id : ids) {
|
||||
result.add(importService.importMovie(String.valueOf(id)));
|
||||
}
|
||||
|
||||
//me.rate(repository.getMovie("13"),5,"Inspiring");
|
||||
me.rate(repository.getMovie("603"),5,"Best of the series");
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void cleanDb() {
|
||||
new Neo4jDatabaseCleaner((AbstractGraphDatabase) ctx.getGraphDatabaseService()).cleanDb();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.neo4j.cineasts.domain.Rating;
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.neo4j.graphdb.traversal.Evaluators;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.kernel.Traversal;
|
||||
import org.neo4j.kernel.Uniqueness;
|
||||
import org.springframework.data.graph.core.EntityPath;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.neo4j.graphdb.DynamicRelationshipType.withName;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.04.11
|
||||
*/
|
||||
public class FriendsMovieRecommendations {
|
||||
private final Map<Movie, MovieRating> ratings = new HashMap<Movie, MovieRating>();
|
||||
private final int ratingDistance;
|
||||
|
||||
public static class MovieRating {
|
||||
int stars;
|
||||
int count;
|
||||
public void update(Rating rating, int weight) {
|
||||
this.stars += rating.getStars()*weight;
|
||||
this.count += weight;
|
||||
}
|
||||
public int average() {
|
||||
return count == 0 ? 0 : stars / count;
|
||||
}
|
||||
public String toString() {
|
||||
return String.valueOf(average());
|
||||
}
|
||||
|
||||
}
|
||||
public FriendsMovieRecommendations(int ratingDistance) {
|
||||
this.ratingDistance = ratingDistance;
|
||||
}
|
||||
|
||||
public Map<Movie, ?> getRecommendationsFor(User user) {
|
||||
TraversalDescription traversal = Traversal.description().breadthFirst()
|
||||
.uniqueness(Uniqueness.NODE_GLOBAL).relationships(withName(User.FRIEND))
|
||||
.evaluator(Evaluators.toDepth(ratingDistance)).evaluator(Evaluators.excludeStartPosition());
|
||||
|
||||
Iterable result = user.findAllPathsByTraversal(traversal);
|
||||
Iterable<EntityPath<User,User>> friends = (Iterable<EntityPath<User,User>>)result;
|
||||
for (EntityPath<User,User> path : friends) {
|
||||
int weight = ratingDistance - path.length();
|
||||
User friend = path.endEntity();
|
||||
aggregateRatings(friend,weight);
|
||||
}
|
||||
for (Rating rating : user.getRatings()) {
|
||||
ratings.remove(rating.getMovie());
|
||||
}
|
||||
return ratings;
|
||||
}
|
||||
|
||||
private void aggregateRatings(User friend, int weight) {
|
||||
for (Rating rating : friend.getRatings()) {
|
||||
obtainRating(rating.getMovie()).update(rating, weight);
|
||||
}
|
||||
}
|
||||
|
||||
private MovieRating obtainRating(Movie movie) {
|
||||
if (!ratings.containsKey(movie)) {
|
||||
ratings.put(movie, new MovieRating());
|
||||
}
|
||||
return ratings.get(movie);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.neo4j.cineasts.domain.User;
|
||||
import org.neo4j.cineasts.repository.MovieRepository;
|
||||
import org.neo4j.graphdb.Path;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.traversal.Evaluation;
|
||||
import org.neo4j.graphdb.traversal.Evaluator;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.kernel.Traversal;
|
||||
import org.neo4j.kernel.Uniqueness;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.neo4j.graphdb.Direction.OUTGOING;
|
||||
import static org.neo4j.graphdb.DynamicRelationshipType.withName;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.04.11
|
||||
*/
|
||||
class MovieRecommendations implements Evaluator {
|
||||
private final Map<Long,int[]> ratings=new HashMap<Long, int[]>();
|
||||
private final int ratingDistance;
|
||||
private MovieRepository movieRepository;
|
||||
|
||||
public MovieRecommendations(MovieRepository movieRepository, int ratingDistance) {
|
||||
this.movieRepository = movieRepository;
|
||||
this.ratingDistance = ratingDistance;
|
||||
}
|
||||
|
||||
public Map<Movie, ?> getRecommendationsFor(User user) {
|
||||
TraversalDescription traversal= Traversal.description().breadthFirst()
|
||||
.uniqueness(Uniqueness.NODE_PATH)
|
||||
.relationships(withName(User.FRIEND))
|
||||
.relationships(withName(User.RATED), OUTGOING)
|
||||
.evaluator(this);
|
||||
return averageRecommendations(user, traversal);
|
||||
}
|
||||
|
||||
private Map<Movie, Integer> averageRecommendations(User user, TraversalDescription traversal) { // todo sort, limit
|
||||
Map<Movie,Integer> result=new HashMap<Movie, Integer>();
|
||||
for (Movie movie : movieRepository.findAllByTraversal(user, traversal)) {
|
||||
final int[] rating = ratings.get(movie.getNodeId());
|
||||
result.put(movie, rating[1]==0 ? 0 : rating[0]/rating[1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Evaluation evaluate(Path path) {
|
||||
final int distance = path.length() - 1;
|
||||
if (distance > ratingDistance) return Evaluation.EXCLUDE_AND_PRUNE;
|
||||
Relationship rated = path.lastRelationship();
|
||||
if (rated != null && rated.getType().name().equals(User.RATED)) {
|
||||
if (distance == 0) return Evaluation.EXCLUDE_AND_PRUNE; // my rated movies
|
||||
updateRating(rated, distance);
|
||||
return Evaluation.INCLUDE_AND_PRUNE;
|
||||
}
|
||||
System.out.println();
|
||||
return Evaluation.EXCLUDE_AND_CONTINUE;
|
||||
}
|
||||
|
||||
private void updateRating(Relationship rated, int distance) {
|
||||
final long movieId = rated.getEndNode().getId();
|
||||
int[] rating = obtainRating(movieId);
|
||||
|
||||
int weight = ratingDistance - distance;
|
||||
final Integer stars = (Integer) rated.getProperty("stars", 0);
|
||||
rating[0] += weight * stars;
|
||||
rating[1] += weight;
|
||||
}
|
||||
|
||||
private int[] obtainRating(long movieId) {
|
||||
if (!ratings.containsKey(movieId)) {
|
||||
ratings.put(movieId,new int[2]);
|
||||
}
|
||||
return ratings.get(movieId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.Transaction;
|
||||
import org.neo4j.graphdb.index.IndexManager;
|
||||
import org.neo4j.kernel.AbstractGraphDatabase;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 02.03.11
|
||||
*/
|
||||
public class Neo4jDatabaseCleaner {
|
||||
private AbstractGraphDatabase graph;
|
||||
|
||||
public Neo4jDatabaseCleaner(AbstractGraphDatabase graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
|
||||
public Map<String, Object> cleanDb() {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
Transaction tx = graph.beginTx();
|
||||
try {
|
||||
removeNodes(result);
|
||||
clearIndex(result);
|
||||
tx.success();
|
||||
} finally {
|
||||
tx.finish();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void removeNodes(Map<String, Object> result) {
|
||||
Node refNode = graph.getReferenceNode();
|
||||
int nodes = 0, relationships = 0;
|
||||
for (Node node : graph.getAllNodes()) {
|
||||
for (Relationship rel : node.getRelationships(Direction.OUTGOING)) {
|
||||
rel.delete();
|
||||
relationships++;
|
||||
}
|
||||
if (!refNode.equals(node)) {
|
||||
node.delete();
|
||||
nodes++;
|
||||
}
|
||||
}
|
||||
result.put("nodes", nodes);
|
||||
result.put("relationships", relationships);
|
||||
|
||||
}
|
||||
|
||||
private void clearIndex(Map<String, Object> result) {
|
||||
IndexManager indexManager = graph.index();
|
||||
result.put("node-indexes", Arrays.asList(indexManager.nodeIndexNames()));
|
||||
result.put("relationship-indexes", Arrays.asList(indexManager.relationshipIndexNames()));
|
||||
for (String ix : indexManager.nodeIndexNames()) {
|
||||
indexManager.forNodes(ix).delete();
|
||||
}
|
||||
for (String ix : indexManager.relationshipIndexNames()) {
|
||||
indexManager.forRelationships(ix).delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
log4j.rootLogger=warn, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
|
||||
#log4j.category.org.springframework.web=DEBUG
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:security="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
|
||||
|
||||
<security:global-method-security secured-annotations="enabled">
|
||||
</security:global-method-security>
|
||||
|
||||
<security:http auto-config="true" access-denied-page="/auth/denied"> <!-- use-expressions="true" -->
|
||||
<security:intercept-url pattern="/admin/*" access="ROLE_ADMIN"/>
|
||||
<security:intercept-url pattern="/import/*" access="ROLE_ADMIN"/>
|
||||
<security:intercept-url pattern="/user/*" access="ROLE_USER"/>
|
||||
<security:intercept-url pattern="/auth/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
|
||||
<security:intercept-url pattern="/auth/register" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
|
||||
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
|
||||
<security:form-login login-page="/auth/login" authentication-failure-url="/auth/login?login_error=true"
|
||||
default-target-url="/user"/>
|
||||
<security:logout logout-url="/auth/logout" logout-success-url="/" invalidate-session="true"/>
|
||||
</security:http>
|
||||
|
||||
<security:authentication-manager>
|
||||
<security:authentication-provider user-service-ref="cineastsUserDetailsService">
|
||||
<security:password-encoder hash="md5">
|
||||
<security:salt-source system-wide="cewuiqwzie"/>
|
||||
</security:password-encoder>
|
||||
</security:authentication-provider>
|
||||
</security:authentication-manager>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:datagraph="http://www.springframework.org/schema/data/graph"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
|
||||
http://www.springframework.org/schema/data/graph http://www.springframework.org/schema/data/graph/datagraph-1.0.xsd">
|
||||
|
||||
<context:annotation-config/>
|
||||
<context:spring-configured/>
|
||||
<context:component-scan base-package="org.neo4j.cineasts">
|
||||
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||
</context:component-scan>
|
||||
|
||||
<datagraph:config storeDirectory="data/graph.db"/>
|
||||
<datagraph:repositories base-package="org.neo4j.cineasts.repository"/>
|
||||
|
||||
<bean class="org.neo4j.cineasts.movieimport.MovieDbApiClient">
|
||||
<constructor-arg value="926d2a79e82920b62f03b1cb57e532e6"/>
|
||||
</bean>
|
||||
<bean class="org.neo4j.cineasts.movieimport.MovieDbLocalStorage">
|
||||
<constructor-arg value="data/json"/>
|
||||
</bean>
|
||||
|
||||
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
|
||||
|
||||
<!--import resource="applicatioContext-security.xml"/-->
|
||||
</beans>
|
||||
@@ -0,0 +1,8 @@
|
||||
<decorators defaultdir="/WEB-INF/views/decorators">
|
||||
<decorator name="frontpage" page="frontpage.jsp">
|
||||
<pattern>/</pattern>
|
||||
</decorator>
|
||||
<decorator name="main" page="main.jsp">
|
||||
<pattern>/*</pattern>
|
||||
</decorator>
|
||||
</decorators>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:jee="http://www.springframework.org/schema/jee"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
|
||||
<mvc:annotation-driven/>
|
||||
<mvc:resources mapping="/images/**" location="/images/"/>
|
||||
<mvc:resources mapping="/resources/**" location="/resources/"/>
|
||||
<context:component-scan base-package="org.neo4j.cineasts.controller"/>
|
||||
|
||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp"/>
|
||||
|
||||
<tx:annotation-driven mode="aspectj"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,35 @@
|
||||
<%@ page session="false" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
|
||||
<%--@elvariable id="actor" type="org.neo4j.cineasts.domain.Actor"--%>
|
||||
<c:choose>
|
||||
<c:when test="${actor != null}">
|
||||
<div class="span-4">
|
||||
<div class="actor-framed-image" style="background-image:url(${actor.profileImageUrl})">
|
||||
</div>
|
||||
<div class="actor-sidebar">
|
||||
<h2>${actor.name}</h2>
|
||||
<p>Born <fmt:formatDate value="${actor.birthday}" pattern="yyyy/dd/MM"/> in ${actor.birthplace}.</p>
|
||||
</div>
|
||||
<div class="break"></div>
|
||||
</div>
|
||||
<div class="span-8 last">
|
||||
<h2>Biography</h2>
|
||||
<p>${actor.biography}</p>
|
||||
<h2>Roles</h2>
|
||||
<c:if test="${not empty actor.roles}">
|
||||
<ul>
|
||||
<c:forEach items="${actor.roles}" var="role">
|
||||
<li>
|
||||
<a href="/movies/${role.movie.id}"><c:out value="${role.movie.title}" /> as <c:out value="${role.name}" /> in <c:out value="${role.movie.year}"/></a><br/>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</c:if>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<h2>Actor cannot be found.</h2>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
@@ -0,0 +1,7 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||||
<html>
|
||||
<head>
|
||||
<title>Access denied</title>
|
||||
</head> <!-- todo redirect after some seconds -->
|
||||
<body>Unfortunately you are not authorized to see this page.</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<%@ page import="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter" %>
|
||||
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Login</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Login</h1>
|
||||
<div class="error">${error}</div>
|
||||
|
||||
<form action="/j_spring_security_check" method="post" >
|
||||
<p>
|
||||
<label for="j_username">Login:</label>
|
||||
<input id="j_username" name="j_username" type="text"
|
||||
<c:if test="${not empty param.login_error}">value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}"</c:if> />
|
||||
</p>
|
||||
<p>
|
||||
<label for="j_password">Password:</label>
|
||||
<input id="j_password" name="j_password" type="password" />
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" name="_spring_security_remember_me"/> Remember me
|
||||
</p>
|
||||
<input type="submit" value="Login"/>
|
||||
</form><br/>
|
||||
<a href="/auth/registerpage">Register</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,37 @@
|
||||
<%@ page import="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter" %>
|
||||
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
|
||||
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Login</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Login</h1>
|
||||
<div class="error">${error}</div>
|
||||
|
||||
<form action="/auth/register" method="post" >
|
||||
<p>
|
||||
<label for="j_username">Login:</label>
|
||||
<input id="j_username" name="j_username" type="text" value="${j_username}"/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="j_displayname">Name:</label>
|
||||
<input id="j_displayname" name="j_displayname" type="text" value="${j_displayname}"/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="j_password">Password:</label>
|
||||
<input id="j_password" name="j_password" type="password" />
|
||||
</p>
|
||||
<input type="submit" value="Register"/>
|
||||
</form>
|
||||
<br/>
|
||||
<a href="/auth/login">Login</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator"
|
||||
prefix="decorator" %>
|
||||
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Cineasts.net - <decorator:title default="Welcome" /></title>
|
||||
<decorator:head />
|
||||
<link rel="stylesheet" type="text/css" href="/resources/style.css">
|
||||
<%@ include file="/WEB-INF/views/includes/style.jsp" %>
|
||||
<%@ include file="/WEB-INF/views/includes/js.jsp" %>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<div id="header-topbar">
|
||||
<div id="header-menu">
|
||||
<%@ include file="/WEB-INF/views/includes/navigation.jsp" %>
|
||||
</div>
|
||||
</div>
|
||||
<div id="header-splash">
|
||||
<img id="logo-splash" src="<c:url value="/images/logo-splash.png"/>" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="content">
|
||||
<decorator:body />
|
||||
</div>
|
||||
<%@ include file="/WEB-INF/views/includes/footer.jsp" %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator"
|
||||
prefix="decorator" %>
|
||||
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Cineasts.net - <decorator:title default="Welcome" /></title>
|
||||
<decorator:head />
|
||||
<link rel="stylesheet" type="text/css" href="/resources/style.css">
|
||||
<%@ include file="/WEB-INF/views/includes/style.jsp" %>
|
||||
<%@ include file="/WEB-INF/views/includes/js.jsp" %>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<div id="header-topbar">
|
||||
<div id="header-menu">
|
||||
<page:applyDecorator name="header_search">
|
||||
<%@ include file="/WEB-INF/views/includes/search.jsp" %>
|
||||
</page:applyDecorator>
|
||||
<%@ include file="/WEB-INF/views/includes/navigation.jsp" %>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/" id="logo"><img src="<c:url value="/images/logo.png"/>" /></a>
|
||||
</div>
|
||||
<div id="content">
|
||||
<decorator:body />
|
||||
</div>
|
||||
<%@ include file="/WEB-INF/views/includes/footer.jsp" %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
<%@ page session="false" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
|
||||
<h2>Results for importing movies: "${ids}"</h2>
|
||||
|
||||
<p>Imported of ${fn:length(movies)} movies took ${duration} seconds.</p>
|
||||
|
||||
<dl>
|
||||
<c:forEach items="${movies}" var="entry">
|
||||
<dt>${entry.key}</dt>
|
||||
<dd>${entry.value}</dd>
|
||||
</c:forEach>
|
||||
</dl>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div id="footer">
|
||||
This site is running on SpringFramework and Spring Data Graph powered by the Neo4j graph database. All movie data is provided by <a href="http://themoviedb.org">themoviedb.org</a>.<br/>
|
||||
<br/>
|
||||
<a href="http://springsource.org"><img src="/images/logo/springsource.png"/></a><a href="http://springsource.org/spring-data"><img src="/images/logo/springdata.jpg"/></a><a href="http://github.com/springsource/spring-data-graph"><img src="/images/logo/springdatagraph.png"/></a><a href="http://neo4j.org"><img src="/images/logo/neo4j.png"/></a>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
|
||||
<div class="navigation">
|
||||
<c:choose>
|
||||
<c:when test="${empty user}">
|
||||
<c:set var="last" value="Login"/>
|
||||
<c:if test="${not empty param.login_error}">
|
||||
<c:set var="last" value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}"/>
|
||||
</c:if>
|
||||
<form action="/j_spring_security_check" method="post" >
|
||||
<input id="j_username" name="j_username" type="text" value="${last}"
|
||||
onfocus="this.value = (this.value=='Login') ? '' : this.value;" onblur="this.value = (this.value=='') ? '${last}' : this.value;"
|
||||
/>
|
||||
<input id="j_password" name="j_password" type="password" onfocus="this.value = (this.value=='*******') ? '' : this.value;" onblur="this.value = (this.value=='') ? '*******' : this.value;" value="*******"/>
|
||||
<input type="image" src="/images/rating-active.png" value="Login" style="max-width:0px;max-height:0px;"/>
|
||||
</form>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<a href="<c:url value="/user/${user.login}" />">${user.name}</a>
|
||||
<a href="<c:url value="/auth/logout" />">Logout</a>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
|
||||
<div class="navigation">
|
||||
<c:choose>
|
||||
<c:when test="${empty user}">
|
||||
<a href="<c:url value="/auth/login" />">Login</a>
|
||||
<a href="<c:url value="/auth/registerpage" />">Register</a>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<a href="<c:url value="/user/${user.login}" />">${user.name}</a>
|
||||
<a href="<c:url value="/auth/logout" />">Logout</a>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="search">
|
||||
<form action="/movies" method="get">
|
||||
<input type="text" name="q" value="Find movie" onfocus="this.value='';" onblur="this.value = (this.value=='') ? 'Find movie' : this.value;"/>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<!-- dynamic, calculated styles -->
|
||||
<style type="text/css">
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<div>
|
||||
<h3>Welcome to Cineasts.net</h3>
|
||||
<div id="big-search-wrap">
|
||||
<form action="/movies" method="get">
|
||||
<input type="text" class="big-search" name="q" value="Find movie" onfocus="this.value='';" onblur="this.value = (this.value=='') ? 'Find movie' : this.value;" />
|
||||
<input type="submit" class="big-search-submit" value="Search"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
<%@ page session="false" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
|
||||
<h2>Results for "${query}"</h2>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${not empty movies}">
|
||||
<ul class="search-results">
|
||||
<c:forEach items="${movies}" var="movie">
|
||||
<li>
|
||||
<div class="search-result-details">
|
||||
<c:set var="image" value="${movie.imageUrl}"/>
|
||||
<c:if test="${empty image}"><c:set var="image" value="/images/movie-placeholder.png"/></c:if>
|
||||
<a class="thumbnail" href="<c:url value="/movies/${movie.id}" />"> <img src="<c:url value="${image}" />" /></a>
|
||||
<a href="/movies/${movie.id}">${movie.title}</a> <img alt="${movie.stars} stars" src="/images/rated_${movie.stars}.png"/>
|
||||
<p><c:out value="${movie.tagline}" escapeXml="true" /></p>
|
||||
</div>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<h2>No movies found for query "${query}".</h2>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
@@ -0,0 +1,170 @@
|
||||
<%@ page session="false" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
|
||||
<html>
|
||||
<head><title>${movie.title}</title></head>
|
||||
<body>
|
||||
<c:choose>
|
||||
<c:when test="${not empty movie}">
|
||||
<div class="span-5">
|
||||
<div class="profile-header">
|
||||
<%--@elvariable id="movie" type="org.neo4j.cineasts.domain.Movie"--%>
|
||||
<c:set var="image" value="${movie.imageUrl}"/>
|
||||
<c:if test="${empty image}"><c:set var="image" value="/images/movie-placeholder.png"/></c:if>
|
||||
<div class="profile-image"><img src="<c:url value="${image}"/>"/></div>
|
||||
<div class="profile-header-details">
|
||||
<h2>${movie.title} (${movie.year}) <img src="/images/rated_${stars}.png" alt="${stars} stars"/></h2>
|
||||
</div>
|
||||
<h3><${movie.tagline}</h3>
|
||||
<div class="break"></div>
|
||||
</div>
|
||||
|
||||
<div class="span-half">
|
||||
<h3>Movie facts</h3>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Language</th>
|
||||
<td>${movie.language}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Runtime</th>
|
||||
<td>${movie.runtime} Minutes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Genre</th>
|
||||
<td>${movie.genre}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Other sites</th>
|
||||
<td><a target="cineasts_link" href="http://www.themoviedb.org/movie/${movie.id}">TheMovieDb.org</a> |
|
||||
<a target="cineasts_link" href="http://www.imdb.com/title/${movie.imdbId}">IMDb</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Buy DVDs & Books</th>
|
||||
<td><a target="cineasts_link" href="http://www.amazon.com/s/?url=search-alias%3Daps&field-keywords=<c:url value="${movie.title}"/>">Amazon</a> |
|
||||
<a target="cineasts_link" href="http://www.cinebutler.com/search.html?searchBy=title&searchFor=<c:url value="${movie.title}"/>">CineButler</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>In Cinemas</th>
|
||||
<td><a target="cineasts_link" href="http://www.google.com/movies?q=<c:url value="${movie.title}"/>">Google Movies</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="span-half last">
|
||||
<c:if test="${not empty movie.trailer}">
|
||||
<h3>Trailers</h3>
|
||||
<c:set var="youtube" value="movie.youtubeId"/>
|
||||
<c:choose>
|
||||
<c:when test="${not empty youtube}">
|
||||
<iframe title="YouTube video player" width="200" height="143" src="http://www.youtube.com/embed/${movie.youtubeId}?rel=0&controls=0&egm=1&fs=1" frameborder="0" allowfullscreen></iframe>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<ul>
|
||||
<li><a href="${movie.trailer}">Trailer</a></li>
|
||||
</ul>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="span-half">
|
||||
<h3>Release info</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Released</th>
|
||||
<td><fmt:formatDate value="${movie.releaseDate}" pattern="yyyy/dd/MM"/></td>
|
||||
</tr>
|
||||
<c:if test="${not empty movie.homepage}">
|
||||
<tr>
|
||||
<th></th>
|
||||
<td><a href="${movie.homepage}">Homepage</a></td>
|
||||
</tr>
|
||||
</c:if>
|
||||
<tr>
|
||||
<th>Studio</th>
|
||||
<td>${movie.studio}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="span-7 last">
|
||||
<div class="movie-content-outer">
|
||||
<div class="movie-content">
|
||||
<h2>Overview</h2>
|
||||
<p>${movie.description}</p>
|
||||
|
||||
<h2>Cast</h2>
|
||||
<c:if test="${not empty movie.roles}">
|
||||
<ul class="actors-list">
|
||||
<c:forEach items="${movie.roles}" var="role">
|
||||
<c:set var="actor" value="${role.actor}"/>
|
||||
<li>
|
||||
<c:set var="image" value="${actor.profileImageUrl}"/>
|
||||
<c:if test="${empty image}"><c:set var="image" value="/images/profile-placeholder-small.png"/></c:if>
|
||||
<a class="actor-image" href="<c:url value="/actors/${actor.id}" />"><img alt="${actor.name}" src="<c:url value="${image}" />" /></a>
|
||||
<a href="<c:url value="/actors/${actor.id}" />"><c:out value="${actor.name}" /> as <c:out value="${role.name}" /></a>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
<div class="break"></div>
|
||||
</c:if>
|
||||
|
||||
<h3>Reviews</h3>
|
||||
<c:if test="${not empty user}">
|
||||
<script type="text/javascript">
|
||||
function rate(n) {
|
||||
var hidden = document.getElementById('rated');
|
||||
hidden.value = n;
|
||||
for (i = 1; i <= 5; i++) {
|
||||
document.getElementById("rated_" + i).src = (i <= n ) ? "/images/rating-active.png" : "/images/rating-disabled.png";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<form method="post" action="<c:url value="/movies/${movie.id}" />">
|
||||
<h4>Give
|
||||
<c:forEach begin="1" end="5" var="i">
|
||||
<a href="#" onClick="rate(${i});"><img src="/images/rating-active.png" id="rated_${i}"/></a>
|
||||
</c:forEach>
|
||||
to "${movie.title}" saying:
|
||||
<input type="hidden" value="${userRating.stars}" name="rated" id="rated"/>
|
||||
<input type="text" size="100" name="comment" value="${userRating.comment}"/>
|
||||
<input type="submit" value="Rate!"/>
|
||||
</h4>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
rate(${userRating.stars});
|
||||
</script>
|
||||
</c:if>
|
||||
<c:if test="${not empty movie.ratings}">
|
||||
<ul>
|
||||
<c:forEach items="${movie.ratings}" var="rating">
|
||||
<c:if test="${rating != userRating}">
|
||||
<li><img src="/images/rated_${rating.stars}.png" alt="${rating.stars} stars"/> by <a href="<c:url value="/user/${rating.user.login}" />">${rating.user.name}</a> who says: "${rating.comment}"</li>
|
||||
</c:if>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</c:if>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<h2>No movie found</h2>
|
||||
<p>The movie you were looking for could not be found.</p>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</body></html>
|
||||
@@ -0,0 +1,103 @@
|
||||
<%--@elvariable id="recommendations" type="java.util.Map<Movie,Float>"--%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||||
<%--@elvariable id="user" type="org.neo4j.cineasts.domain.User"--%>
|
||||
<html>
|
||||
<head>
|
||||
<title>Profile</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="span-5">
|
||||
<div class="profile-header">
|
||||
<div class="profile-image"><img src="<c:url value="/images/profile-placeholder.png" />" /></div>
|
||||
<div class="profile-header-details">
|
||||
<h2>Hi ${user.name}!</h2>
|
||||
<p>${user.info}</p>
|
||||
</div>
|
||||
<div class="break"></div>
|
||||
</div>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${not empty user.friends}">
|
||||
<h2>Your friends</h2>
|
||||
<ul class="friends-list">
|
||||
<c:forEach items="${user.friends}" var="friend">
|
||||
<li>
|
||||
<a class="friend-image" href="<c:url value="/user/${friend.login}" />"><img src="<c:url value="/images/profile-placeholder-small.png" />" /></a>
|
||||
<div class="friend-info">
|
||||
<h3><a href="<c:url value="/user/${friend.login}" />"><c:out value="${friend.name}"/></a></h3>
|
||||
<p>${friend.info}</p>
|
||||
</div>
|
||||
<div class="break"></div>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<h2>You don't have any friends yet</h2>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
<div class="span-7 last">
|
||||
<div class="profile-feed">
|
||||
<div class="span-third">
|
||||
<h2>Your rated movies</h2>
|
||||
</div>
|
||||
<c:set var="ratings" value="${user.ratings}"/>
|
||||
<div class="span-third last">
|
||||
<h2>${fn:length(ratings)}</h2>
|
||||
</div>
|
||||
<ul class="rated-movies-list span-all last">
|
||||
<c:choose>
|
||||
<c:when test="${not empty ratings}">
|
||||
<c:forEach items="${ratings}" var="rating">
|
||||
<c:set var="movie" value="${rating.movie}"/>
|
||||
<c:set var="stars" value="${rating.stars}"/>
|
||||
<li>
|
||||
<h4><a href="<c:url value="/movies/${movie.id}" />"><c:out value="${movie.title}"/>
|
||||
(${movie.year}) - "${rating.comment}"</a>
|
||||
<img class="rating" src="/images/rated_${stars}.png" alt="${stars} stars"/>
|
||||
</h4>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
You have not rated any movies.
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</ul>
|
||||
<div class="break"></div>
|
||||
|
||||
<div class="span-third">
|
||||
<h2>Your recommendations</h2>
|
||||
</div>
|
||||
<div class="span-third last">
|
||||
<h2>${fn:length(recommendations)}</h2>
|
||||
</div>
|
||||
<ul class="rated-movies-list span-all last">
|
||||
<c:choose>
|
||||
<c:when test="${not empty recommendations}">
|
||||
<c:forEach items="${recommendations}" var="recommendation">
|
||||
<c:set var="movie" value="${recommendation.key}"/>
|
||||
<c:set var="stars" value="${recommendation.value}"/>
|
||||
<li>
|
||||
<h4><a href="<c:url value="/movies/${movie.id}" />"><c:out value="${movie.title}"/>
|
||||
(${movie.year}) - "${movie.tagline}"</a>
|
||||
<img class="rating" src="/images/rated_${stars}.png" alt="${stars} stars"/>
|
||||
</h4>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
There are no recommendations for you, perhaps you have to add some friends?
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</ul>
|
||||
<div class="break"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,86 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
|
||||
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||||
<%--@elvariable id="user" type="org.neo4j.cineasts.domain.User"--%>
|
||||
<c:set var="name" value="${profiled.name}"/>
|
||||
<html>
|
||||
<head><title>Profile for ${name}</title></head>
|
||||
<body>
|
||||
|
||||
<div class="span-5">
|
||||
<div class="profile-header">
|
||||
<div class="profile-image"><img src="<c:url value="/images/profile-placeholder.png" />" /></div>
|
||||
<div class="profile-header-details">
|
||||
<h2>${name}</h2>
|
||||
<p>${profiled.info}</p>
|
||||
</div>
|
||||
<div class="break"></div>
|
||||
</div>
|
||||
|
||||
<h2>${name}'s friends</h2>
|
||||
<c:choose>
|
||||
<c:when test="${isFriend}">
|
||||
<c:set var="friends" value="${profiled.friends}"/>
|
||||
<c:choose>
|
||||
<c:when test="${not empty friends}">
|
||||
<ul class="friends-list">
|
||||
<c:forEach items="${friends}" var="friend">
|
||||
<li>
|
||||
<a class="friend-image" href="<c:url value="/user/${friend.login}" />"><img src="<c:url value="/images/profile-placeholder-small.png" />" /></a>
|
||||
<div class="friend-info">
|
||||
<h3><a href="<c:url value="/user/${friend.login}" />"><c:out value="${friend.name}"/></a></h3>
|
||||
<p>${friend.info}</p>
|
||||
</div>
|
||||
<div class="break"></div>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
${name} has no friends.
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<form id="add_friend" action="/user/${profiled.login}/friends" method="post">
|
||||
<a href="#" onClick="document.getElementById('add_friend').submit();return false;">Add ${name} as a friend.</a>
|
||||
</form>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
<c:set var="ratings" value="${profiled.ratings}"/>
|
||||
<div class="span-7 last">
|
||||
<div class="profile-feed">
|
||||
<div class="span-third">
|
||||
<h2>${name}'s rated movies</h2>
|
||||
</div>
|
||||
<div class="span-third last">
|
||||
<h2>${fn:length(ratings)}</h2>
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="rated-movies-list span-all last">
|
||||
<c:choose>
|
||||
<c:when test="${not empty ratings}">
|
||||
<c:forEach items="${ratings}" var="rating">
|
||||
<c:set var="movie" value="${rating.movie}"/>
|
||||
<c:set var="stars" value="${rating.stars}"/>
|
||||
<li>
|
||||
<h4><a href="<c:url value="/movies/${movie.id}" />"><c:out value="${movie.title}"/>
|
||||
(${movie.year}) - "${rating.comment}"</a>
|
||||
<img class="rating" src="/images/rated_${stars}.png" alt="${stars} stars"/>
|
||||
</h4>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
${name} has not rated any movies.
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</ul>
|
||||
<div class="break"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
|
||||
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>
|
||||
/WEB-INF/applicatioContext-security.xml
|
||||
/WEB-INF/applicationContext.xml
|
||||
</param-value>
|
||||
</context-param>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<filter>
|
||||
<filter-name>sitemesh</filter-name>
|
||||
<filter-class>
|
||||
com.opensymphony.module.sitemesh.filter.PageFilter
|
||||
</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>sitemesh</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>dispatcherServlet</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>dispatcherServlet</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<session-config>
|
||||
<session-timeout>60</session-timeout>
|
||||
</session-config>
|
||||
</web-app>
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 538 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1023 B |
|
After Width: | Height: | Size: 588 B |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 214 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,13 @@
|
||||
label {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Label for radio buttons, normal buttons and checkboxes.
|
||||
*/
|
||||
label.button-label {
|
||||
display:inline;
|
||||
float:none;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
.span-1 { width: 6.958%; float:left; margin-right:1.5%; }
|
||||
.span-2 { width: 15.416%; float:left; margin-right:1.5%; }
|
||||
.span-3 { width: 23.875%; float:left; margin-right:1.5%; }
|
||||
.span-4 { width: 32.333%; float:left; margin-right:1.5%; }
|
||||
.span-5 { width: 40.791%; float:left; margin-right:1.5%; }
|
||||
.span-6 { width: 49.250%; float:left; margin-right:1.5%; }
|
||||
.span-7 { width: 57.708%; float:left; margin-right:1.5%; }
|
||||
.span-8 { width: 66.166%; float:left; margin-right:1.5%; }
|
||||
.span-9 { width: 74.625%; float:left; margin-right:1.5%; }
|
||||
.span-10 { width: 83.083%; float:left; margin-right:1.5%; }
|
||||
.span-11 { width: 91.541%; float:left; margin-right:1.5%; }
|
||||
.span-12 { width: 100%; float:left; margin-right:0%; }
|
||||
|
||||
.span-third { width: 32%; float:left; margin-right:2%; }
|
||||
.span-half { width: 49%; float:left; margin-right:2%; }
|
||||
.span-all { width: 100%; float:left; clear:right; }
|
||||
|
||||
.last { clear:right; margin-right:0; }
|
||||
@@ -0,0 +1,328 @@
|
||||
|
||||
@import url(typography.css);
|
||||
@import url(forms.css);
|
||||
@import url(grid.css);
|
||||
|
||||
* {
|
||||
outline-style: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
background : #010100;
|
||||
}
|
||||
|
||||
a img { border:0; }
|
||||
|
||||
/**
|
||||
* MAIN COMPONENTS
|
||||
*/
|
||||
|
||||
#header {
|
||||
background: url(../images/header-curtain.png) repeat-y bottom center #010100;
|
||||
border-bottom: 1px solid #5C5C45;
|
||||
}
|
||||
|
||||
#logo img {
|
||||
padding: 10px 10px 0;
|
||||
}
|
||||
|
||||
#header-topbar {
|
||||
height: 49px;
|
||||
padding: 0 0 0 10px;
|
||||
background: url(../images/filmroll-horizontal.png) repeat-x bottom left transparent;
|
||||
}
|
||||
|
||||
#header-splash {
|
||||
text-align:center;
|
||||
background: url(../images/header-splash-bg.png) repeat-x bottom center;
|
||||
}
|
||||
|
||||
#logo-splash {
|
||||
position:relative;
|
||||
top:4px;
|
||||
}
|
||||
|
||||
|
||||
#header-menu {
|
||||
float: right;
|
||||
color: #DDDDDD;
|
||||
height:28px;
|
||||
background:#0b0f07;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
.search {
|
||||
float:left;
|
||||
padding:0 10px;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.navigation a {
|
||||
color: #d8d8d8;
|
||||
text-decoration:none;
|
||||
padding: 0 0 0 5px;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
text-align:center;
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
#footer img {
|
||||
padding: 10px;
|
||||
}
|
||||
/**
|
||||
* SEARCH RESULTS
|
||||
*/
|
||||
|
||||
.search-results > li {
|
||||
border-top: 1px solid #666666;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.search-result-details {
|
||||
/* float:left;
|
||||
padding: 0px 10px;
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* PROFILE, ACTOR AND MOVIE PAGES
|
||||
*/
|
||||
|
||||
.profile-header {
|
||||
clear:both;
|
||||
}
|
||||
|
||||
.profile-image {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.profile-image img {
|
||||
width: 100px;
|
||||
}
|
||||
.actor-image img, .profile-image img, .friend-image img, .thumbnail img {
|
||||
border: 1px solid #4C5C2A !important;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
display:block;
|
||||
/* float:left;*/
|
||||
}
|
||||
.thumbnail img {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.actor-framed-image {
|
||||
background: url(../images/actor-picture-frame.png) no-repeat;
|
||||
width:166px;
|
||||
height:214px;
|
||||
padding: 6px 60px 16px;
|
||||
}
|
||||
|
||||
.actor-sidebar {
|
||||
padding: 0 56px;
|
||||
}
|
||||
|
||||
.profile-header-details {
|
||||
float: left;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.profile-feed {
|
||||
background: none repeat scroll 0 0 #252525;
|
||||
border: 5px solid #CACF81;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.facts {
|
||||
width:50%;
|
||||
float:left;
|
||||
}
|
||||
|
||||
.movie-content-outer, .actor-content-outer {
|
||||
background: url(../images/filmroll-vertical-full.png) no-repeat top right;
|
||||
padding-right: 38px;
|
||||
}
|
||||
|
||||
.movie-content, .actor-content {
|
||||
background: url(../images/filmroll-vertical-full.png) no-repeat top left;
|
||||
padding: 0 10px 0 48px;
|
||||
min-height:690px;
|
||||
}
|
||||
|
||||
.actors-list li {
|
||||
width:100px;
|
||||
float:left;
|
||||
}
|
||||
|
||||
.actors-list img {
|
||||
height:92px;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.friends-list li {
|
||||
border-top:1px solid #333;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.friends-list .friend-image {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.friend-image img {
|
||||
height: 64px;
|
||||
}
|
||||
.friends-list .friend-info {
|
||||
float:left;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.friends-list h3, .search-results h3 {
|
||||
border:none;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.friends-list h3 a, .search-results h3 a {
|
||||
text-decoration:none;
|
||||
color: #99CC33;
|
||||
}
|
||||
|
||||
.rated-movies-list h3 {
|
||||
margin: 0;
|
||||
padding: 5px 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.rated-movies-list h3 a {
|
||||
color: #99CC33;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.rated-movies-list li {
|
||||
position:relative;
|
||||
height:20px;
|
||||
padding:8px 0;
|
||||
}
|
||||
|
||||
.rated-movies-list .rating {
|
||||
/* position:absolute;
|
||||
right:0;
|
||||
top:5px;
|
||||
*/
|
||||
float: right;
|
||||
}
|
||||
|
||||
/**
|
||||
* BIG SEARCH
|
||||
*/
|
||||
|
||||
#big-search-wrap {
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.big-search {
|
||||
background: url("../images/searchbar-big.png") no-repeat scroll 0 0 transparent;
|
||||
border: 0 none;
|
||||
height: 50px;
|
||||
width: 480px;
|
||||
font-size: 22px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.big-search-submit {
|
||||
background: url("../images/searchbar-big-searchicon.png") no-repeat scroll 0 0 transparent;
|
||||
border: 0 none;
|
||||
color: transparent;
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
left: -75px;
|
||||
position: relative;
|
||||
width: 41px;
|
||||
}
|
||||
|
||||
/**
|
||||
* RATINGS
|
||||
*/
|
||||
|
||||
.rating-input li, .rating-input lh {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.rating {
|
||||
display: block;
|
||||
height: 20px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.rating li {
|
||||
height: 28px;
|
||||
width: 22px;
|
||||
display:block;
|
||||
float:left;
|
||||
padding:0 2px;
|
||||
}
|
||||
|
||||
.rating li.active {
|
||||
background: url("/images/rating-active.png") no-repeat scroll center 0 transparent;
|
||||
}
|
||||
|
||||
.rating li.disabled {
|
||||
/* background: url("/images/rating-disabled.png") no-repeat scroll center 0 transparent; */
|
||||
}
|
||||
|
||||
/**
|
||||
* TABLES
|
||||
*/
|
||||
|
||||
table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/**
|
||||
* LISTS
|
||||
*/
|
||||
|
||||
ul {
|
||||
list-style:none;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
lh {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* UTILS
|
||||
*/
|
||||
|
||||
.break {
|
||||
clear:both;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ERRORS
|
||||
*/
|
||||
|
||||
.error {
|
||||
color:red;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #99CC33;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
body {
|
||||
font-family:tahoma,verdana,sans-serif,serif;
|
||||
color: #666666;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #314718; /*#99CC33;*/
|
||||
font-size: 16px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #314718; /*#ff6600;*/
|
||||
font-size: 14px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #333333;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.tiny-link {
|
||||
color: #222;
|
||||
font-size:12px;
|
||||
text-decoration:none;
|
||||
}
|
||||
.tiny-link:hover {
|
||||
text-decoration:underline;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.neo4j.cineasts.domain;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.cineasts.repository.MovieRepository;
|
||||
import org.neo4j.cineasts.repository.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration({"/movies-test-context.xml"})
|
||||
@Transactional
|
||||
public class DomainTest {
|
||||
|
||||
@Autowired
|
||||
protected MovieRepository movieRepository;
|
||||
@Autowired
|
||||
protected UserRepository userRepository;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actorCanPlayARoleInAMovie() {
|
||||
Person tomHanks = new Person("1","Tom Hanks").persist();
|
||||
Movie forestGump = new Movie("1", "Forrest Gump").persist();
|
||||
|
||||
Role role = tomHanks.playedIn(forestGump, "Forrest");
|
||||
|
||||
Movie foundForestGump = this.movieRepository.findByPropertyValue(null, "id", "1");
|
||||
|
||||
assertEquals("created and looked up movie equal", forestGump, foundForestGump);
|
||||
Role firstRole = foundForestGump.getRoles().iterator().next();
|
||||
assertEquals("role forrest",role, firstRole);
|
||||
assertEquals("role forrest","Forrest", firstRole.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canFindMovieByTitleQuery() {
|
||||
Movie forestGump = new Movie("1", "Forrest Gump").persist();
|
||||
Iterator<Movie> queryResults = movieRepository.findAllByQuery("search", "title", "Forre*").iterator();
|
||||
assertTrue("found movie by query",queryResults.hasNext());
|
||||
Movie foundMovie = queryResults.next();
|
||||
assertEquals("created and looked up movie equal", forestGump, foundMovie);
|
||||
assertFalse("found only one movie by query", queryResults.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userCanRateMovie() {
|
||||
Movie movie= new Movie("1","Forrest Gump").persist();
|
||||
User user = new User("ich","Micha","password").persist();
|
||||
Rating awesome = user.rate(movie, 5, "Awesome");
|
||||
|
||||
|
||||
User foundUser = userRepository.findByPropertyValue("login", "ich");
|
||||
Rating rating = user.getRatings().iterator().next();
|
||||
assertEquals(awesome,rating);
|
||||
assertEquals("Awesome",rating.getComment());
|
||||
assertEquals(5,rating.getStars());
|
||||
assertEquals(5,movie.getStars(),0);
|
||||
}
|
||||
@Test
|
||||
public void canFindUserByLogin() {
|
||||
User user = new User("ich","Micha","password").persist();
|
||||
User foundUser = userRepository.findByPropertyValue("login", "ich");
|
||||
assertEquals(user, foundUser);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 13.03.11
|
||||
*/
|
||||
public class MovieDbApiClientTest {
|
||||
private static final String API_KEY = "926d2a79e82920b62f03b1cb57e532e6";
|
||||
|
||||
@Test
|
||||
public void testGetMovie() throws Exception {
|
||||
Map movie = new MovieDbApiClient(API_KEY).getMovie("2");
|
||||
assertEquals(2,movie.get("id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPerson() throws Exception {
|
||||
Map person = new MovieDbApiClient(API_KEY).getPerson("30112");
|
||||
assertEquals(30112,person.get("id"));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.neo4j.cineasts.domain.Person;
|
||||
import org.neo4j.cineasts.service.CineastsRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 13.03.11
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration({"/movies-test-context.xml"})
|
||||
@Transactional
|
||||
public class MovieDbImportServiceTest {
|
||||
|
||||
@Autowired
|
||||
MovieDbImportService importService;
|
||||
|
||||
@Autowired
|
||||
CineastsRepository cineastsRepository;
|
||||
|
||||
@Test
|
||||
public void testImportMovie() throws Exception {
|
||||
Movie movie = importService.importMovie("2");
|
||||
assertEquals("movie-id","2", movie.getId());
|
||||
assertEquals("movie-title","Ariel", movie.getTitle());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportMovieTwice() throws Exception {
|
||||
Movie movie = importService.importMovie("2");
|
||||
Movie movie2 = importService.importMovie("2");
|
||||
final Movie foundMovie = cineastsRepository.getMovie("2");
|
||||
assertEquals("movie-id", movie, foundMovie);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportPerson() throws Exception {
|
||||
Person actor = importService.importPerson("105955");
|
||||
assertEquals("movie-id","105955", actor.getId());
|
||||
assertEquals("movie-title","George M. Williamson", actor.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 13.03.11
|
||||
*/
|
||||
public class MovieDbLocalStorageTest {
|
||||
|
||||
|
||||
private static final String ID = "111";
|
||||
private static final Map<String,String> DATA = Collections.singletonMap("id", ID);
|
||||
protected MovieDbLocalStorage storage;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
storage = new MovieDbLocalStorage("target/data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasMovie() throws Exception {
|
||||
storage.storeMovie(ID,DATA);
|
||||
assertEquals(true, storage.hasMovie(ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadMovie() throws Exception {
|
||||
storage.storeMovie(ID,DATA);
|
||||
assertEquals(DATA,storage.loadMovie(ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreMovie() throws Exception {
|
||||
storage.storeMovie(ID,DATA);
|
||||
assertEquals(true, new File("target/data/movie_"+ID+".json").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasPerson() throws Exception {
|
||||
storage.storePerson(ID,DATA);
|
||||
assertEquals(true, storage.hasPerson(ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadPerson() throws Exception {
|
||||
storage.storePerson(ID,DATA);
|
||||
assertEquals(DATA,storage.loadPerson(ID));
|
||||
}
|
||||
@Test
|
||||
public void testLoadPersonFromList() throws Exception {
|
||||
storage.storePerson(ID,asList(DATA));
|
||||
assertEquals(DATA,storage.loadPerson(ID));
|
||||
}
|
||||
@Test
|
||||
public void testLoadPersonFromInvalidList() throws Exception {
|
||||
storage.storePerson(ID, asList("Nothing found."));
|
||||
final Map personData = storage.loadPerson(ID);
|
||||
assertTrue("person unexpectedly found", personData.containsKey("not_found"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStorePerson() throws Exception {
|
||||
storage.storePerson(ID,DATA);
|
||||
assertEquals(true, new File("target/data/person_"+ID+".json").exists());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.neo4j.cineasts.service;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.cineasts.domain.Movie;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 04.03.11
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration({"/movies-test-context.xml"})
|
||||
@Transactional
|
||||
public class MoviesRepositoryTest {
|
||||
@Autowired
|
||||
CineastsRepository repository;
|
||||
|
||||
@Test
|
||||
public void testGetMovie() throws Exception {
|
||||
Movie movie = new Movie("1", "Test-Movie").persist();
|
||||
Movie found = repository.getMovie("1");
|
||||
assertEquals("movie found by id", movie, found);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindTwoMovies() throws Exception {
|
||||
Movie movie1 = new Movie("1", "Test-Movie1").persist();
|
||||
Movie movie2 = new Movie("2", "Test-Movie2").persist();
|
||||
Movie movie3 = new Movie("3", "Another-Movie3").persist();
|
||||
List<Movie> found = repository.findMovies("Test*", 2);
|
||||
assertEquals("2 movies found",2,found.size());
|
||||
assertEquals("2 correct movies found by query", new HashSet<Movie>(asList(movie1, movie2)), new HashSet<Movie>(found));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindTwoMoviesButRestrictToOne() throws Exception {
|
||||
Movie movie1 = new Movie("1", "Test-Movie1").persist();
|
||||
Movie movie2 = new Movie("2", "Test-Movie2").persist();
|
||||
Movie movie3 = new Movie("3", "Another-Movie3").persist();
|
||||
List<Movie> found = repository.findMovies("Test*", 1);
|
||||
assertEquals("1 movie found",1,found.size());
|
||||
assertTrue("1 correct movie found by query", found.get(0).getTitle().startsWith("Test"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:jee="http://www.springframework.org/schema/jee"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:datagraph="http://www.springframework.org/schema/data/graph"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/data/graph http://www.springframework.org/schema/data/graph/datagraph-1.0.xsd">
|
||||
|
||||
<context:annotation-config/>
|
||||
<context:spring-configured/>
|
||||
<context:component-scan base-package="org.neo4j.cineasts"/>
|
||||
|
||||
<datagraph:config graphDatabaseService="graphDatabaseService"/>
|
||||
<datagraph:repositories base-package="org.neo4j.cineasts.repository"/>
|
||||
|
||||
<bean id="graphDatabaseService" class="org.neo4j.kernel.ImpermanentGraphDatabase"/>
|
||||
|
||||
<bean class="org.neo4j.cineasts.movieimport.MovieDbApiClient">
|
||||
<constructor-arg value="926d2a79e82920b62f03b1cb57e532e6"/>
|
||||
</bean>
|
||||
<bean class="org.neo4j.cineasts.movieimport.MovieDbLocalStorage">
|
||||
<constructor-arg value="target/json-data"/>
|
||||
</bean>
|
||||
<tx:annotation-driven mode="aspectj"/>
|
||||
</beans>
|
||||
10
spring-data-neo4j-examples/hello-worlds/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
lib
|
||||
.ivy
|
||||
.gradle
|
||||
target
|
||||
*.ipr
|
||||
*.iml
|
||||
*.iws
|
||||
.classpath
|
||||
.project
|
||||
|
||||
1
spring-data-neo4j-examples/hello-worlds/.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
||||
spring-data-neo4j-hello-worlds
|
||||
7
spring-data-neo4j-examples/hello-worlds/.idea/ant.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AntConfiguration">
|
||||
<defaultAnt bundledAnt="true" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
28
spring-data-neo4j-examples/hello-worlds/.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AjcSettings">
|
||||
<option name="ajcPath" value="$MAVEN_REPOSITORY$/org/aspectj/aspectjtools/1.6.12.M1/aspectjtools-1.6.12.M1.jar" />
|
||||
<option name="cmdLineParams" value="-target 1.6" />
|
||||
</component>
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="DEFAULT_COMPILER" value="ajc" />
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="?*.properties" />
|
||||
<entry name="?*.xml" />
|
||||
<entry name="?*.gif" />
|
||||
<entry name="?*.png" />
|
||||
<entry name="?*.jpeg" />
|
||||
<entry name="?*.jpg" />
|
||||
<entry name="?*.html" />
|
||||
<entry name="?*.dtd" />
|
||||
<entry name="?*.tld" />
|
||||
<entry name="?*.ftl" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing enabled="false" useClasspath="true" />
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_STRING" value="-target 1.6" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||