Skip to content

Instantly share code, notes, and snippets.

@acmi
Created May 27, 2013 14:18
Show Gist options
  • Save acmi/5657312 to your computer and use it in GitHub Desktop.
Save acmi/5657312 to your computer and use it in GitHub Desktop.
vk view
.msg-low{
-fx-background-color: aquamarine;
}
.msg-mid{
-fx-background-color: gold;
}
.msg-high{
-fx-background-color: indianred;
}
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'
}
}
}
}
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}
}
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)
}
}
<?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