Browse code

add html importer

devnewton authored on 20/12/2015 at 20:16:33
Showing 9 changed files
... ...
@@ -39,6 +39,11 @@
39 39
             <version>0.2.8</version>
40 40
         </dependency>
41 41
         <dependency>
42
+            <groupId>org.jsoup</groupId>
43
+            <artifactId>jsoup</artifactId>
44
+            <version>1.8.3</version>
45
+        </dependency>
46
+        <dependency>
42 47
             <groupId>org.springframework.boot</groupId>
43 48
             <artifactId>spring-boot-starter-test</artifactId>
44 49
             <scope>test</scope>
... ...
@@ -3,16 +3,23 @@ package im.bci.passgrid.controllers;
3 3
 import im.bci.passgrid.data.Passgrid;
4 4
 import im.bci.passgrid.data.PassgridRepository;
5 5
 import im.bci.passgrid.frontend.CreatePassgridRQ;
6
+import im.bci.passgrid.frontend.ImportResultMV;
7
+import im.bci.passgrid.importer.HtmlGridImporter;
6 8
 import java.io.IOException;
7 9
 import java.io.PrintWriter;
10
+import java.util.ArrayList;
11
+import java.util.List;
8 12
 import java.util.Random;
9 13
 import javax.servlet.http.HttpServletResponse;
14
+import org.jboss.logging.Logger;
10 15
 import org.springframework.beans.factory.annotation.Autowired;
11 16
 import org.springframework.stereotype.Controller;
12 17
 import org.springframework.ui.Model;
13 18
 import org.springframework.web.bind.annotation.PathVariable;
14 19
 import org.springframework.web.bind.annotation.RequestMapping;
15 20
 import org.springframework.web.bind.annotation.RequestMethod;
21
+import org.springframework.web.bind.annotation.RequestParam;
22
+import org.springframework.web.multipart.MultipartFile;
16 23
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
17 24
 
18 25
 @Controller
... ...
@@ -22,6 +29,9 @@ public class PassgridController {
22 22
     @Autowired
23 23
     private PassgridRepository passgridRepository;
24 24
 
25
+    @Autowired
26
+    private HtmlGridImporter htmlGridImporter;
27
+
25 28
     @RequestMapping("")
26 29
     public String index(Model model) {
27 30
         return "index";
... ...
@@ -94,4 +104,28 @@ public class PassgridController {
94 94
         }
95 95
         return grid;
96 96
     }
97
+
98
+    @RequestMapping(value = "import", method = RequestMethod.POST)
99
+    public String importGrid(Model model, RedirectAttributes attributes, @RequestParam("file") MultipartFile[] files) throws IOException {
100
+        List<ImportResultMV> results = new ArrayList<ImportResultMV>();
101
+        for (int f = 0; f < files.length; ++f) {
102
+            MultipartFile file = files[f];
103
+            ImportResultMV result = new ImportResultMV();
104
+            result.setFilename(null != file.getOriginalFilename() ? file.getOriginalFilename() : "file " + (f + 1));
105
+            try {
106
+                result.setPassgrids(htmlGridImporter.importGrids(file.getInputStream()));
107
+            } catch (Exception e) {
108
+                Logger.getLogger(PassgridController.class.getName()).warn(e);
109
+                result.setError(e.getMessage());
110
+            }
111
+            results.add(result);
112
+        }
113
+        attributes.addFlashAttribute("importResults", results);
114
+        return "redirect:/import-results";
115
+    }
116
+
117
+    @RequestMapping("import-results")
118
+    public String importGridResults() {
119
+        return "import-results";
120
+    }
97 121
 }
... ...
@@ -38,4 +38,15 @@ public class Passgrid {
38 38
     public Character[][] getGrid() {
39 39
         return grid;
40 40
     }
41
+
42
+    public boolean isEmpty() {
43
+        if (null != grid) {
44
+            for (Character[] row : grid) {
45
+                if (row.length > 0) {
46
+                    return false;
47
+                }
48
+            }
49
+        }
50
+        return true;
51
+    }
41 52
 }
42 53
new file mode 100644
... ...
@@ -0,0 +1,41 @@
0
+package im.bci.passgrid.frontend;
1
+
2
+import im.bci.passgrid.data.Passgrid;
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+
6
+/**
7
+ *
8
+ * @author devnewton
9
+ */
10
+public class ImportResultMV {
11
+
12
+    private String filename;
13
+    private List<Passgrid> passgrids = new ArrayList<Passgrid>();
14
+    private String error;
15
+
16
+    public String getFilename() {
17
+        return filename;
18
+    }
19
+
20
+    public void setFilename(String filename) {
21
+        this.filename = filename;
22
+    }
23
+
24
+    public List<Passgrid> getPassgrids() {
25
+        return passgrids;
26
+    }
27
+
28
+    public void setPassgrids(List<Passgrid> passgrids) {
29
+        this.passgrids = passgrids;
30
+    }
31
+
32
+    public String getError() {
33
+        return error;
34
+    }
35
+
36
+    public void setError(String error) {
37
+        this.error = error;
38
+    }
39
+
40
+}
0 41
new file mode 100644
... ...
@@ -0,0 +1,130 @@
0
+package im.bci.passgrid.importer;
1
+
2
+import im.bci.passgrid.data.Passgrid;
3
+import im.bci.passgrid.data.PassgridRepository;
4
+import java.io.IOException;
5
+import java.io.InputStream;
6
+import java.util.ArrayList;
7
+import java.util.List;
8
+import org.jsoup.Jsoup;
9
+import org.jsoup.nodes.Document;
10
+import org.jsoup.nodes.Element;
11
+import org.jsoup.parser.Parser;
12
+import org.jsoup.select.Elements;
13
+import org.springframework.beans.factory.annotation.Autowired;
14
+import org.springframework.stereotype.Component;
15
+
16
+/**
17
+ *
18
+ * @author devnewton
19
+ */
20
+@Component
21
+public class HtmlGridImporter {
22
+
23
+    private int firstRow = 2, firstColumn = 1;
24
+    private int nameRow = 0, nameColumn = 0;
25
+
26
+    @Autowired
27
+    private PassgridRepository passgridRepository;
28
+
29
+    public List<Passgrid> importGrids(InputStream inputStream) throws IOException {
30
+        Document doc = Jsoup.parse(inputStream, null, "");
31
+        Elements tables = doc.select("table");
32
+        List<Passgrid> results = new ArrayList<Passgrid>();
33
+        for (Element table : tables) {
34
+            Passgrid passgrid = importGridFromTable(table);
35
+            checkGrid(passgrid);
36
+            passgridRepository.save(passgrid);
37
+            results.add(passgrid);
38
+        }
39
+        return results;
40
+    }
41
+
42
+    private Passgrid importGridFromTable(Element table) {
43
+        Passgrid result = new Passgrid();
44
+        result.setName(retrieveGridName(table));
45
+        Elements trs = table.select("tr");
46
+        if (trs.size() > firstRow) {
47
+            List<Element> rows = trs.subList(firstRow, trs.size());
48
+            int rowCount = rows.size();
49
+            int columnCount = countColumns(rows);
50
+            Character[][] grid = new Character[rowCount][columnCount];
51
+            for (int r = 0; r < rowCount; ++r) {
52
+                Elements tds = rows.get(r).select("td,th");
53
+                for (int c = 0; c < columnCount; ++c) {
54
+                    int index = c + firstColumn;
55
+                    char ch = retrieveCellChar(index, tds);
56
+                    grid[r][c] = ch;
57
+                }
58
+            }
59
+            result.setGrid(grid);
60
+        }
61
+        return result;
62
+    }
63
+
64
+    private char retrieveCellChar(int index, Elements tds) {
65
+        char ch = ' ';
66
+        if (index < tds.size()) {
67
+            String text = tds.get(index).text();
68
+            if (!text.isEmpty()) {
69
+                ch = text.charAt(0);
70
+            }
71
+        }
72
+        return ch;
73
+    }
74
+
75
+    private int countColumns(List<Element> rows) {
76
+        int columnCount = 0;
77
+        for (Element row : rows) {
78
+            Elements tds = row.select("td,th");
79
+            columnCount = Math.max(columnCount, tds.size() - firstColumn);
80
+        }
81
+        return columnCount;
82
+    }
83
+
84
+    public int getFirstColumn() {
85
+        return firstColumn;
86
+    }
87
+
88
+    public void setFirstColumn(int firstColumn) {
89
+        this.firstColumn = firstColumn;
90
+    }
91
+
92
+    public int getFirstRow() {
93
+        return firstRow;
94
+    }
95
+
96
+    public void setFirstRow(int firstRow) {
97
+        this.firstRow = firstRow;
98
+    }
99
+
100
+    public int getNameRow() {
101
+        return nameRow;
102
+    }
103
+
104
+    public void setNameRow(int nameRow) {
105
+        this.nameRow = nameRow;
106
+    }
107
+
108
+    public int getNameColumn() {
109
+        return nameColumn;
110
+    }
111
+
112
+    public void setNameColumn(int nameColumn) {
113
+        this.nameColumn = nameColumn;
114
+    }
115
+
116
+    private String retrieveGridName(Element table) {
117
+        return table.select("tr").get(nameRow).select("td,th").get(nameColumn).text();
118
+    }
119
+
120
+    private void checkGrid(Passgrid passgrid) {
121
+        if (null == passgrid.getName()) {
122
+            throw new RuntimeException("Could not import grid name");
123
+        }
124
+        if(passgrid.isEmpty()) {
125
+            throw new RuntimeException("Could not import grid content");
126
+        }
127
+    }
128
+
129
+}
0 130
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+<!DOCTYPE html>
1
+<html>
2
+    {{> partials/head title="import results"}}
3
+    <body>
4
+        {{> partials/header}}
5
+        {{#each importResults}}
6
+        <div>
7
+            <h2>{{filename}}</h2>
8
+            {{#each passgrids}}
9
+            <div>
10
+                {{> partials/passgrid this}}
11
+            </div>
12
+            {{/each}}
13
+            {{error}}
14
+        </div>
15
+        {{/each}}
16
+    </body>
17
+</html>
... ...
@@ -4,5 +4,6 @@
4 4
     <body>
5 5
         {{> partials/header}}
6 6
         {{> partials/forms/create}}
7
+        {{> partials/forms/import}}
7 8
     </body>
8 9
 </html>
9 10
\ No newline at end of file
... ...
@@ -1,5 +1,5 @@
1 1
 <h2>Create grid</h2>
2
-<form action="/create" method="POST">
2
+<form action="/create" method="post">
3 3
     <label for="create-passgrid-name" >Name: </label>
4 4
     <input id="create-passgrid-name" type="text" name="name">
5 5
     <label for="create-passgrid-allowed-characters" >Lines: </label>
6 6
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+<h2>Import grids from html files</h2>
1
+<form action="/import" method="post" enctype="multipart/form-data">
2
+    <label for="files-to-import" >Files: </label>
3
+    <input id="files-to-import" type="file" name="file" multiple>
4
+    <input type="hidden" name="{{_csrf.parameterName}}" value="{{_csrf.token}}"/>
5
+    <input type="submit">
6
+</form>
0 7
\ No newline at end of file