Created
May 27, 2013 14:18
-
-
Save acmi/5657312 to your computer and use it in GitHub Desktop.
vk view
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.msg-low{ | |
-fx-background-color: aquamarine; | |
} | |
.msg-mid{ | |
-fx-background-color: gold; | |
} | |
.msg-high{ | |
-fx-background-color: indianred; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package vkparser | |
import com.vk.api.groups.GroupsCommon | |
import com.vk.api.other.OtherCommon | |
import com.vk.api.users.UsersCommon | |
import com.vk.api.wall.WallCommon | |
import com.vk.api.Info | |
import com.vk.worker.impl.VKWorkerGroup | |
import com.vk.api.wall.Post | |
import groovy.util.logging.Log | |
import groovy.xml.MarkupBuilder | |
import javafx.application.Platform | |
import javafx.beans.binding.StringBinding | |
import javafx.beans.property.IntegerProperty | |
import javafx.beans.property.SimpleIntegerProperty | |
import javafx.beans.property.StringProperty | |
import javafx.beans.value.ChangeListener | |
import javafx.beans.value.ObservableValue | |
import javafx.concurrent.Task | |
import javafx.concurrent.Worker | |
import javafx.concurrent.WorkerStateEvent | |
import javafx.event.ActionEvent | |
import javafx.event.EventHandler | |
import javafx.fxml.FXML | |
import javafx.fxml.Initializable | |
import javafx.geometry.Insets | |
import javafx.geometry.Pos | |
import javafx.scene.Scene | |
import javafx.scene.control.Button | |
import javafx.scene.control.ContextMenu | |
import javafx.scene.control.Label | |
import javafx.scene.control.ListView | |
import javafx.scene.control.MenuItem | |
import javafx.scene.control.RadioButton | |
import javafx.scene.control.TextArea | |
import javafx.scene.control.TextField | |
import javafx.scene.layout.VBoxBuilder | |
import javafx.scene.web.WebEngine | |
import javafx.scene.web.WebView | |
import javafx.stage.Modality | |
import javafx.stage.Stage | |
import javafx.stage.WindowEvent | |
import java.text.DateFormat | |
import java.text.SimpleDateFormat | |
import java.util.logging.Level | |
/** | |
* @author acmi | |
*/ | |
@Log | |
class Controller implements Initializable { | |
private static String XML_FILE = 'words.xml' | |
private static final DateFormat DATE_FORMAT = new SimpleDateFormat('HH:mm:ss dd/MM/yyyy') | |
@FXML | |
private TextField wallId | |
@FXML | |
private ListView<RadioButton> themes | |
@FXML | |
private Label messagesCount | |
@FXML | |
private Label parsed | |
@FXML | |
private ListView<PostView> messages | |
@FXML | |
private TextField newThemeName | |
private Map themeWords = [:] | |
private VKWorkerGroup vkEngine | |
private Map<String, StringProperty> names = [:] | |
@Override | |
void initialize(URL url, ResourceBundle resourceBundle) { | |
StringBinding sb = new StringBinding() { | |
{ | |
bind(messages.items) | |
} | |
@Override | |
protected String computeValue() { | |
return "Count: ${messages.items.size()}" | |
} | |
} | |
messagesCount.textProperty().bind(sb) | |
loadThemesfromXML() | |
reloadThemeList() | |
} | |
void initVKEngine(String accessToken) { | |
vkEngine = new VKWorkerGroup(accessToken) | |
} | |
void openVKLogin() { | |
Stage vkLoginStage = new Stage(); | |
vkLoginStage.initModality(Modality.APPLICATION_MODAL) | |
WebView wv = new WebView() | |
final WebEngine engine = wv.getEngine() | |
engine.load('https://oauth.vk.com/authorize?' + [ | |
client_id: 3535407, | |
scope: ['friends', 'wall', 'status'].join(','), | |
redirect_uri: 'http://oauth.vk.com/blank.html', | |
display: 'popup', | |
response_type: 'token', | |
].collect { k, v -> "${k}=${v}" }.join('&')) | |
engine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { | |
public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State t1) { | |
println t1 | |
String vkURL = engine.getLocation(); | |
println vkURL | |
int i; | |
if ((i = vkURL.indexOf("access_token")) != -1) { | |
StringTokenizer st = new StringTokenizer(vkURL.substring(i), "&"); | |
String token = st.nextToken(); | |
token = token.substring(token.indexOf("=") + 1); | |
String expire = st.nextToken(); | |
expire = expire.substring(expire.indexOf("=") + 1); | |
String user = st.nextToken(); | |
user = user.substring(user.indexOf("=") + 1); | |
initVKEngine(token) | |
vkLoginStage.close() | |
} | |
} | |
}) | |
vkLoginStage.onCloseRequest = new EventHandler<WindowEvent>() { | |
@Override | |
void handle(WindowEvent t) { | |
if (vkEngine == null) | |
close() | |
} | |
} | |
vkLoginStage.title = 'VK Login' | |
vkLoginStage.scene = new Scene(wv, 600, 400) | |
vkLoginStage.show() | |
} | |
@FXML | |
private void addTheme(ActionEvent action) { | |
themeWords[newThemeName.text] = [] | |
reloadThemeList() | |
} | |
private void loadThemesfromXML() { | |
try { | |
XmlSlurper xml = new XmlSlurper() | |
def theme_words = xml.parse(new File(XML_FILE)) | |
theme_words.theme.each { | |
themeWords[it.'@name'.toString()] = it.word.collect { | |
it.text() | |
} | |
} | |
log.info("themes loaded: $themeWords") | |
} catch (IOException e) { | |
log.log(Level.WARNING, 'Couldn\'t load words file', e) | |
} | |
} | |
private void saveThemesToXML() { | |
try { | |
new File(XML_FILE).withWriter { | |
MarkupBuilder xml = new MarkupBuilder(it) | |
xml.theme_words() { | |
themeWords.each { key, value -> | |
theme(name: key) { | |
value.each { w -> | |
word(w) | |
} | |
} | |
} | |
} | |
it.close() | |
} | |
} catch (IOException e) { | |
log.log(Level.WARNING, 'Couldn\'t save words file', e) | |
} | |
} | |
private void reloadThemeList() { | |
themes.items.clear() | |
themeWords.each { | |
final String name = it.key | |
RadioButton rb = new RadioButton(name) | |
ContextMenu cm = new ContextMenu() | |
MenuItem edit = new MenuItem('Edit') | |
edit.onAction = new EventHandler<ActionEvent>() { | |
@Override | |
void handle(ActionEvent t) { | |
final Stage dialogStage = new Stage(); | |
dialogStage.initModality(Modality.APPLICATION_MODAL); | |
final TextArea words = new TextArea(themeWords[name].join('\n')) | |
Button ok = new Button('Ok') | |
ok.onAction = new EventHandler<ActionEvent>() { | |
@Override | |
void handle(ActionEvent t1) { | |
List ws = Controller.this.themeWords[name] | |
ws.clear() | |
ws.addAll(words.text.readLines()) | |
saveThemesToXML() | |
dialogStage.close() | |
} | |
} | |
dialogStage.setScene(new Scene(VBoxBuilder.create(). | |
children(words, ok). | |
alignment(Pos.CENTER).padding(new Insets(5)).build())); | |
dialogStage.show(); | |
} | |
} | |
cm.items << edit | |
MenuItem delete = new MenuItem('Delete') | |
delete.onAction = new EventHandler<ActionEvent>() { | |
@Override | |
void handle(ActionEvent t) { | |
themeWords.remove(name) | |
reloadThemeList() | |
saveThemesToXML() | |
} | |
} | |
cm.items << delete | |
rb.contextMenu = cm | |
themes.items << rb | |
} | |
} | |
@FXML | |
private void parse(ActionEvent action) { | |
Button parseBtn = action.source | |
parseBtn.disable = true | |
messages.items.clear() | |
final Task task = new ParseTask() | |
task.onSucceeded = new EventHandler<WorkerStateEvent>() { | |
@Override | |
void handle(WorkerStateEvent t) { | |
parseBtn.disable = false | |
} | |
} | |
Thread t = new Thread(task) | |
t.daemon = true | |
t.start() | |
} | |
private int calcN(String str) { | |
int n = 0 | |
themes.items.split { it.selected }[0].each { | |
themeWords[it.text].each { word -> | |
n += str.findAll(word).size() | |
} | |
} | |
n | |
} | |
@FXML | |
private void close() { | |
if (vkEngine != null) | |
shutdownVKEngine() | |
Platform.exit() | |
} | |
private class ParseTask extends Task { | |
IntegerProperty parsedCount = new SimpleIntegerProperty() | |
ParseTask() { | |
StringBinding parsedText = new StringBinding() { | |
{ | |
bind(parsedCount) | |
} | |
@Override | |
protected String computeValue() { | |
return "(${parsedCount.value.toString()})" | |
} | |
} | |
Platform.runLater(new Runnable() { | |
@Override | |
void run() { | |
Controller.this.parsed.textProperty().bind(parsedText) | |
} | |
}) | |
} | |
@Override | |
protected Object call() throws Exception { | |
println System.getProperty('file.encoding') | |
wallId.text.split(';').each { range -> | |
def r = range.split('-') | |
if (r.length == 2) | |
r = [r[0].toInteger(), r[1].toInteger()] | |
(r[0]..r[-1]).each { id_str -> | |
try { | |
Info info = null | |
use(OtherCommon){ | |
info = vkEngine.getInfo(id_str) | |
} | |
if (info != null) | |
use(WallCommon, UsersCommon, GroupsCommon, OtherCommon){ | |
Iterator<Post> wallIterator = vkEngine.get(info.id) | |
while (wallIterator.hasNext()) { | |
def post = wallIterator.next() | |
Platform.runLater(new Runnable() { | |
@Override | |
void run() { | |
parsedCount.set(parsedCount.get() + 1) | |
} | |
}) | |
def n = calcN(post.text) | |
if (n > 0) { | |
def from = vkEngine.getInfoById(post.from) | |
def to = vkEngine.getInfoById(post.to) | |
Platform.runLater(new Runnable() { | |
@Override | |
void run() { | |
PostView pv = new PostView( | |
from, | |
to, | |
post.date, | |
post.text, | |
) | |
pv.styleClass << getStyleClass(n) | |
Controller.this.messages.items << pv | |
Controller.this.messages.items.sort { | |
calcN(it.postText) | |
} | |
} | |
}) | |
} | |
} | |
} | |
} catch (Exception e) { | |
log.log(Level.WARNING, e.toString()) | |
} | |
} | |
} | |
null | |
} | |
private static String getStyleClass(int n) { | |
switch (n) { | |
case 1..3: return 'msg-low' | |
case 4..10: return 'msg-mid' | |
default: return 'msg-high' | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package vkparser | |
import javafx.scene.control.TextArea | |
import java.text.DateFormat | |
import java.text.SimpleDateFormat | |
/** | |
* @author acmi | |
*/ | |
class PostView extends TextArea { | |
private static final DateFormat DATE_FORMAT = new SimpleDateFormat('HH:mm:ss dd/MM/yyyy') | |
private String postText | |
PostView(from, to, Date date, String text) { | |
this.postText = text | |
setText "${to}\n${from} (${DATE_FORMAT.format(date)})\n\n${text.replaceAll('<br>', '\n')}" | |
setEditable false | |
setWrapText true | |
} | |
String getPostText(){postText} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package vkparser | |
import groovy.util.logging.Log | |
import javafx.application.Application | |
import javafx.fxml.FXMLLoader | |
import javafx.scene.Scene | |
import javafx.stage.Stage | |
import java.util.logging.LogManager | |
/** | |
* @author scared | |
*/ | |
@Log | |
class Window extends Application { | |
@Override | |
void start(Stage stage) throws Exception { | |
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource('/window.fxml')) | |
stage.title = 'VK Parser' | |
stage.scene = new Scene(fxmlLoader.load()) | |
stage.show() | |
Controller controller = fxmlLoader.controller | |
def args = getParameters().named | |
if (args.containsKey('access_token')){ | |
controller.initVKEngine(args['access_token']) | |
}else{ | |
controller.openVKLogin() | |
} | |
} | |
static main(args) { | |
LogManager.logManager.readConfiguration(new FileInputStream('log.ini')) | |
launch(Window.class, args) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?import java.lang.*?> | |
<?import java.net.*?> | |
<?import java.util.*?> | |
<?import javafx.geometry.*?> | |
<?import javafx.scene.control.*?> | |
<?import javafx.scene.layout.*?> | |
<?import javafx.scene.paint.*?> | |
<BorderPane id="BorderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="450.0" prefWidth="800.0" xmlns:fx="http://javafx.com/fxml" fx:controller="vkparser.Controller"> | |
<center> | |
<SplitPane dividerPositions="0.7506265664160401" focusTraversable="true" prefHeight="160.0" prefWidth="200.0" BorderPane.margin="$x1"> | |
<items> | |
<VBox id="VBox" alignment="CENTER" spacing="5.0"> | |
<children> | |
<HBox id="HBox" alignment="CENTER" fillHeight="false" minHeight="-Infinity" prefHeight="26.0" prefWidth="585.0" spacing="5.0"> | |
<children> | |
<Label fx:id="messagesCount" text="messagesCount" /> | |
<Label fx:id="parsed" text="(0)" /> | |
</children> | |
</HBox> | |
<ListView fx:id="messages" prefHeight="374.0" prefWidth="242.0" VBox.vgrow="ALWAYS" /> | |
</children> | |
<padding> | |
<Insets fx:id="x1" /> | |
</padding> | |
</VBox> | |
<VBox prefHeight="200.0" prefWidth="100.0" spacing="5.0"> | |
<children> | |
<HBox fillHeight="true" minHeight="-Infinity" minWidth="-1.0" prefHeight="26.0" prefWidth="192.0" spacing="1.0"> | |
<children> | |
<TextField fx:id="newThemeName" prefWidth="124.0" HBox.hgrow="ALWAYS" /> | |
<Button minWidth="-Infinity" mnemonicParsing="false" onAction="#addTheme" prefWidth="80.0" text="Add theme" /> | |
</children> | |
<padding> | |
<Insets top="1.0" /> | |
</padding> | |
<VBox.margin> | |
<Insets left="2.0" right="2.0" fx:id="x2" /> | |
</VBox.margin> | |
</HBox> | |
<ListView fx:id="themes" prefHeight="303.0" prefWidth="167.0" VBox.vgrow="ALWAYS" /> | |
</children> | |
</VBox> | |
</items> | |
</SplitPane> | |
</center> | |
<stylesheets> | |
<URL value="@msg.css" /> | |
</stylesheets> | |
<top> | |
<VBox prefHeight="-1.0" prefWidth="-1.0"> | |
<children> | |
<MenuBar> | |
<menus> | |
<Menu mnemonicParsing="false" text="File"> | |
<items> | |
<MenuItem mnemonicParsing="false" onAction="#close" text="Close" /> | |
</items> | |
</Menu> | |
</menus> | |
</MenuBar> | |
<HBox prefHeight="-1.0" prefWidth="-1.0" spacing="1.0" VBox.margin="$x2"> | |
<children> | |
<TextField fx:id="wallId" prefWidth="200.0" text="is_vm_41" HBox.hgrow="ALWAYS" /> | |
<Button fx:id="parseBtn" minWidth="-Infinity" mnemonicParsing="false" onAction="#parse" prefWidth="80.0" text="Parse" HBox.hgrow="NEVER" /> | |
</children> | |
</HBox> | |
</children> | |
</VBox> | |
</top> | |
</BorderPane> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment