Jean-Francois Leveque

Preview release processing

...@@ -37,8 +37,7 @@ public class CosineRecommender implements Recommender { ...@@ -37,8 +37,7 @@ public class CosineRecommender implements Recommender {
37 37
38 ItemRecommender irec; 38 ItemRecommender irec;
39 39
40 - @Override 40 + public CosineRecommender(String filePath) {
41 - public void Recommender(String filePath) {
42 config = new LenskitConfiguration(); 41 config = new LenskitConfiguration();
43 42
44 config.bind(ItemScorer.class).to(ItemItemScorer.class); 43 config.bind(ItemScorer.class).to(ItemItemScorer.class);
......
...@@ -33,8 +33,7 @@ public class FunkSVDRecommender implements Recommender { ...@@ -33,8 +33,7 @@ public class FunkSVDRecommender implements Recommender {
33 33
34 ItemRecommender irec; 34 ItemRecommender irec;
35 35
36 - @Override 36 + public FunkSVDRecommender(String filePath) {
37 - public void Recommender(String filePath) {
38 config = new LenskitConfiguration(); 37 config = new LenskitConfiguration();
39 38
40 config.bind(ItemScorer.class).to(FunkSVDItemScorer.class); 39 config.bind(ItemScorer.class).to(FunkSVDItemScorer.class);
......
...@@ -37,8 +37,7 @@ public class PearsonRecommender implements Recommender { ...@@ -37,8 +37,7 @@ public class PearsonRecommender implements Recommender {
37 37
38 ItemRecommender irec; 38 ItemRecommender irec;
39 39
40 - @Override 40 + public PearsonRecommender(String filePath) {
41 - public void Recommender(String filePath) {
42 config = new LenskitConfiguration(); 41 config = new LenskitConfiguration();
43 42
44 config.bind(ItemScorer.class).to(ItemItemScorer.class); 43 config.bind(ItemScorer.class).to(ItemItemScorer.class);
......
...@@ -10,18 +10,8 @@ import org.springframework.context.annotation.Bean; ...@@ -10,18 +10,8 @@ import org.springframework.context.annotation.Bean;
10 @SpringBootApplication 10 @SpringBootApplication
11 public class ProcessingApplication { 11 public class ProcessingApplication {
12 12
13 - Logger logger = LoggerFactory.getLogger(getClass());
14 -
15 public static void main(String[] args) { 13 public static void main(String[] args) {
16 SpringApplication.run(ProcessingApplication.class, args); 14 SpringApplication.run(ProcessingApplication.class, args);
17 } 15 }
18 16
19 - @Bean
20 - public CommandLineRunner process() {
21 - return (args) -> this.run();
22 - }
23 -
24 - public void run() {
25 -
26 - }
27 } 17 }
......
1 +package org.legrog.recommendation.process;
2 +
3 +import org.grouplens.lenskit.scored.ScoredId;
4 +import org.slf4j.Logger;
5 +import org.slf4j.LoggerFactory;
6 +
7 +import java.util.List;
8 +import java.util.Set;
9 +
10 +public class ProcessingExpert {
11 + Logger logger = LoggerFactory.getLogger(getClass());
12 +
13 + Recommender recommender;
14 + int topSize;
15 +
16 + public ProcessingExpert(Recommender recommender, int topSize) {
17 + this.recommender = recommender;
18 + this.topSize = topSize;
19 + }
20 +
21 + public ProcessingRecommendations getRecommendations(Set<Long> userIds) {
22 + ProcessingRecommendations processingRecommendations = new ProcessingRecommendations();
23 + List<ScoredId> recommendations;
24 + for (Long userId : userIds) {
25 + recommendations = recommender.recommend(userId, topSize);
26 + for (ScoredId recommendation : recommendations) {
27 + processingRecommendations.addRecommentation(new RecommendationElement(userId, recommendation.getId()));
28 + logger.trace("Recommending {} for {}", recommendation.getId(), userId);
29 + }
30 + }
31 +
32 + return processingRecommendations;
33 + }
34 +}
1 +package org.legrog.recommendation.process;
2 +
3 +import java.util.HashSet;
4 +import java.util.Set;
5 +
6 +public class ProcessingRecommendations {
7 + Set<RecommendationElement> recommendations;
8 +
9 + public ProcessingRecommendations() {
10 + recommendations = new HashSet<>();
11 + }
12 +
13 + public void addRecommentation(RecommendationElement recommendationElement) {
14 + recommendations.add(recommendationElement);
15 + }
16 +
17 + public Set<RecommendationElement> getRecommendations() {
18 + return recommendations;
19 + }
20 +}
1 +package org.legrog.recommendation.process;
2 +
3 +import org.apache.commons.csv.CSVFormat;
4 +import org.apache.commons.csv.CSVPrinter;
5 +import org.apache.commons.csv.CSVRecord;
6 +import org.slf4j.Logger;
7 +import org.slf4j.LoggerFactory;
8 +import org.springframework.beans.factory.annotation.Value;
9 +import org.springframework.boot.ApplicationArguments;
10 +import org.springframework.boot.ApplicationRunner;
11 +import org.springframework.stereotype.Component;
12 +
13 +import java.io.*;
14 +import java.util.HashSet;
15 +import java.util.Properties;
16 +import java.util.Set;
17 +
18 +@Component
19 +public class ProcessingRunner implements ApplicationRunner {
20 + Logger logger = LoggerFactory.getLogger(getClass());
21 +
22 +
23 + @Value("${parameters.filename}")
24 + private String parametersFilename;
25 +
26 + @Value("${data.dir}")
27 + private String dataDir;
28 +
29 + @Value("${collectionSample.filename}")
30 + private String collectionSampleFilename;
31 +
32 + @Value("${ratingSample.filename}")
33 + private String ratingSampleFilename;
34 +
35 + @Value("${recommandations.filename}")
36 + private String recommandationsFilename;
37 +
38 + private String sampleFilename;
39 + private String algorithm;
40 +
41 + private Recommender recommender;
42 +
43 + private int topSize;
44 +
45 + @Override
46 + public void run(ApplicationArguments args) throws Exception {
47 + loadParameters();
48 + logger.trace("Parameters loaded");
49 + Set<Long> userIds = loadUserIdsFromSample();
50 + RecommenderFactory recommenderFactory = new RecommenderFactory();
51 + recommender = recommenderFactory.build(algorithm ,dataDir+sampleFilename);
52 + logger.trace("Recommender built");
53 + ProcessingExpert processingExpert = new ProcessingExpert(recommender, topSize);
54 + ProcessingRecommendations processingRecommendations = processingExpert.getRecommendations(userIds);
55 + logger.trace("Recommendations done");
56 + writeRecommendationsToFile(processingRecommendations);
57 + logger.trace("Recommendations written");
58 + }
59 +
60 + private Set<Long> loadUserIdsFromSample() throws ProcessingException {
61 + Set<Long> userIds = new HashSet<>();
62 +
63 + Reader in = null;
64 + try {
65 + in = new FileReader(dataDir+sampleFilename);
66 + Iterable<CSVRecord> records = CSVFormat.TDF.withFirstRecordAsHeader().parse(in);
67 + for (CSVRecord record : records) {
68 + userIds.add(Long.parseLong(record.get("userId")));
69 + }
70 + } catch (FileNotFoundException e) {
71 + throw new ProcessingException("sample file not found " + dataDir + sampleFilename, e);
72 + } catch (IOException e) {
73 + throw new ProcessingException("Can't read user ids from sample file " + dataDir + sampleFilename, e);
74 + }
75 +
76 + return userIds;
77 + }
78 +
79 + private void writeRecommendationsToFile(ProcessingRecommendations processingRecommendations) throws ProcessingException {
80 + try {
81 + CSVPrinter csvPrinter = new CSVPrinter(new FileWriter(new File(dataDir, recommandationsFilename)),
82 + CSVFormat.TDF.withHeader("itemId", "userId"));
83 + // TODO : finish
84 + Set<RecommendationElement> recommendations = processingRecommendations.getRecommendations();
85 + if (recommendations.isEmpty()) {
86 + logger.trace("No recommendations at all");
87 + } else {
88 + logger.trace("{} recommendations", recommendations.size());
89 + }
90 + // forEach incompatible avec IOException
91 + for (RecommendationElement recommendation : recommendations) {
92 + csvPrinter.printRecord(recommendation.getItemId(), recommendation.getUserId());
93 + }
94 + csvPrinter.close();
95 + } catch (IOException e) {
96 + throw new ProcessingException("Can't write recommendations file " + dataDir + recommandationsFilename, e);
97 + }
98 + }
99 +
100 + /**
101 + * read properties file from application.properties' parameter.fileName then search for parameters inside
102 + * for ratings, select rating or collection file as the sample file for sample ; using others directly
103 + *
104 + * todo replace this by a command line switch ?
105 + *
106 + * @throws ProcessingException
107 + */
108 + private void loadParameters() throws ProcessingException {
109 + try (InputStream in = new FileInputStream(new File(dataDir, parametersFilename))) {
110 + Properties properties = new Properties();
111 + properties.load(in);
112 + if (properties.containsKey("ratings")) {
113 + logger.trace("ratings {}", properties.getProperty("ratings"));
114 + if (Boolean.parseBoolean(properties.getProperty("ratings"))) {
115 + sampleFilename = ratingSampleFilename;
116 + } else {
117 + sampleFilename = collectionSampleFilename;
118 + }
119 + } else {
120 + // by default, takes collection
121 + sampleFilename = collectionSampleFilename;
122 + }
123 + if (properties.containsKey("algorithm")) {
124 + algorithm = properties.getProperty("algorithm");
125 + } else {
126 + // default algorithm is Cosine
127 + algorithm = "cosine";
128 + }
129 + if (properties.containsKey("topSize")) {
130 + topSize = Integer.parseInt(properties.getProperty("topSize"));
131 + } else {
132 + // default top size is 10
133 + topSize = 10;
134 + }
135 + } catch (IOException e) {
136 + throw new ProcessingException("Can't read parameters properties file " + parametersFilename, e);
137 + }
138 + }
139 +
140 + private class ProcessingException extends Exception {
141 + public ProcessingException() {
142 + super();
143 + }
144 +
145 + public ProcessingException(String message) {
146 + super(message);
147 + }
148 +
149 + public ProcessingException(String message, Throwable cause) {
150 + super(message, cause);
151 + }
152 +
153 + public ProcessingException(Throwable cause) {
154 + super(cause);
155 + }
156 +
157 + protected ProcessingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
158 + super(message, cause, enableSuppression, writableStackTrace);
159 + }
160 + }
161 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package org.legrog.recommendation.process;
2 +
3 +public class RecommendationElement {
4 + private Long userId;
5 + private Long itemId;
6 +
7 + public RecommendationElement(Long userId, Long itemId) {
8 + this.userId = userId;
9 + this.itemId = itemId;
10 + }
11 +
12 + public Long getUserId() {
13 + return userId;
14 + }
15 +
16 + public Long getItemId() {
17 + return itemId;
18 + }
19 +}
...@@ -5,7 +5,5 @@ import org.grouplens.lenskit.scored.ScoredId; ...@@ -5,7 +5,5 @@ import org.grouplens.lenskit.scored.ScoredId;
5 import java.util.List; 5 import java.util.List;
6 6
7 public interface Recommender { 7 public interface Recommender {
8 -
9 - void Recommender(String filePath);
10 List<ScoredId> recommend(long user, int n); 8 List<ScoredId> recommend(long user, int n);
11 } 9 }
......
1 +package org.legrog.recommendation.process;
2 +
3 +import org.springframework.stereotype.Component;
4 +
5 +@Component
6 +public class RecommenderFactory {
7 +
8 + public Recommender build(String recommenderName, String filePath) {
9 + Recommender recommender;
10 +
11 + if (recommenderName.equals("cosine")) {
12 + recommender = new CosineRecommender(filePath);
13 + } else if (recommenderName.equals("funkSVD")) {
14 + recommender = new FunkSVDRecommender(filePath);
15 + } else if (recommenderName.equals("pearson")) {
16 + recommender = new PearsonRecommender(filePath);
17 + } else {
18 + recommender = new SlopeOneRecommender(filePath);
19 + }
20 +
21 + return recommender;
22 + }
23 +}
...@@ -30,8 +30,7 @@ public class SlopeOneRecommender implements Recommender { ...@@ -30,8 +30,7 @@ public class SlopeOneRecommender implements Recommender {
30 30
31 ItemRecommender irec; 31 ItemRecommender irec;
32 32
33 - @Override 33 + public SlopeOneRecommender(String filePath) {
34 - public void Recommender(String filePath) {
35 config = new LenskitConfiguration(); 34 config = new LenskitConfiguration();
36 35
37 config.bind(ItemScorer.class).to(SlopeOneItemScorer.class); 36 config.bind(ItemScorer.class).to(SlopeOneItemScorer.class);
......
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<configuration>
3 +
4 + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
5 + <!-- encoders are assigned the type
6 + ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
7 + <encoder>
8 + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
9 + </encoder>
10 + </appender>
11 +
12 + <logger name="org.legrog" level="DEBUG"/>
13 + <logger name="org.legrog.recommendation.process" level="TRACE"/>
14 +
15 + <root level="warn">
16 + <appender-ref ref="STDOUT" />
17 + </root>
18 +</configuration>
...\ No newline at end of file ...\ No newline at end of file