/* * WPCleaner: A tool to help on Wikipedia maintenance tasks. * Copyright (C) 2013 Nicolas Vervelle * * See README.txt file for licensing information. */ package org.wikipediacleaner.gui.swing; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ScrollPaneConstants; import javax.swing.WindowConstants; import org.wikipediacleaner.api.API; import org.wikipediacleaner.api.APIException; import org.wikipediacleaner.api.APIFactory; import org.wikipediacleaner.api.RecentChangesListener; import org.wikipediacleaner.api.constants.EnumWikipedia; import org.wikipediacleaner.api.constants.WPCConfiguration; import org.wikipediacleaner.api.constants.WPCConfigurationLong; import org.wikipediacleaner.api.data.DataManager; import org.wikipediacleaner.api.data.Namespace; import org.wikipediacleaner.api.data.Page; import org.wikipediacleaner.api.data.RecentChange; import org.wikipediacleaner.gui.swing.action.ActionDispose; import org.wikipediacleaner.gui.swing.basic.BasicWindow; import org.wikipediacleaner.gui.swing.worker.UpdateDabWarningTools; import org.wikipediacleaner.gui.swing.worker.UpdateWarningTools.Stats; import org.wikipediacleaner.i18n.GT; /** * A window for monitoring Recent Changes. */ public class MonitorRCWindow extends BasicWindow implements RecentChangesListener { /** * Table model for the list of recent changes. */ private RecentChangesTableModel modelRC; /** * Table for the list of recent changes. */ private JTable tableRC; /** * Table model for the list of recent changes that are interesting. */ private RecentChangesTableModel modelRCInteresting; /** * Table for the list of recent changes. */ private JTable tableRCInteresting; /** * Map for monitored pages (Title => Last modified time). */ private Map<String, Long> monitoredPages; /** * Tools for creating disambiguation warning. */ private UpdateDabWarningTools createDabWarning; /** * Tools for updating disambiguation warning. */ private UpdateDabWarningTools updateDabWarning; /** * Create and display a window for monitoring recent changes. * * @param wiki Wiki. */ public static void createMonitorRCWindow( final EnumWikipedia wiki) { createWindow( "MonitorRCWindow", wiki, WindowConstants.DISPOSE_ON_CLOSE, MonitorRCWindow.class, null); } /** * @return Title of the window. * @see org.wikipediacleaner.gui.swing.basic.BasicWindow#getTitle() */ @Override public String getTitle() { return GT._("Recent changes monitoring"); } /** * @return Window components. */ @Override protected Component createComponents() { JPanel panel = new JPanel(new GridBagLayout()); // Initialize constraints GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridheight = 1; constraints.gridwidth = 1; constraints.gridx = 0; constraints.gridy = 0; constraints.insets = new Insets(0, 0, 0, 0); constraints.ipadx = 0; constraints.ipady = 0; constraints.weightx = 0; constraints.weighty = 0; // Recent changes table constraints.fill = GridBagConstraints.BOTH; constraints.weightx = 1; constraints.weighty = 1; modelRC = new RecentChangesTableModel(null); tableRC = new JTable(modelRC); modelRC.configureColumnModel(tableRC.getColumnModel()); JScrollPane scrollRC = new JScrollPane(tableRC); scrollRC.setMinimumSize(new Dimension(300, 200)); scrollRC.setPreferredSize(new Dimension(800, 300)); scrollRC.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); panel.add(scrollRC, constraints); constraints.gridy++; // Interesting recent changes table constraints.fill = GridBagConstraints.BOTH; constraints.weightx = 1; constraints.weighty = 1; modelRCInteresting = new RecentChangesTableModel(null); tableRCInteresting = new JTable(modelRCInteresting); modelRCInteresting.configureColumnModel(tableRCInteresting.getColumnModel()); JScrollPane scrollRCInteresting = new JScrollPane(tableRCInteresting); scrollRCInteresting.setMinimumSize(new Dimension(300, 200)); scrollRCInteresting.setPreferredSize(new Dimension(800, 300)); scrollRCInteresting.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); panel.add(scrollRCInteresting, constraints); constraints.gridy++; // Buttons JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); JButton buttonClose = ActionDispose.createButton(getParentComponent(), true, false); buttonPanel.add(buttonClose); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 0; constraints.weightx = 1; constraints.weighty = 0; panel.add(buttonPanel, constraints); constraints.gridy++; updateComponentState(); monitoredPages = new HashMap<String, Long>(); createDabWarning = new UpdateDabWarningTools(getWikipedia(), this, true); updateDabWarning = new UpdateDabWarningTools(getWikipedia(), this, false); API api = APIFactory.getAPI(); api.addRecentChangesListener(getWikipedia(), this); return panel; } /** * Dispose window. * @see org.wikipediacleaner.gui.swing.basic.BasicWindow#dispose() */ @Override public void dispose() { API api = APIFactory.getAPI(); api.removeRecentChangesListener(getWikipedia(), this); super.dispose(); } /** * Callback to be notified about recent changes. * * @param newRC List of recent changes. * @param currentTime Current time. * @see org.wikipediacleaner.api.RecentChangesListener#recentChanges(java.util.List, java.util.Date) */ @Override public void recentChanges(List<RecentChange> newRC, Date currentTime) { // Retrieve configuration WPCConfiguration config = getWikipedia().getConfiguration(); long delayForNew = config.getLong(WPCConfigurationLong.RC_NEW_ARTICLE_WITH_DAB_DELAY) * 60 * 1000; long delayMonitoring = config.getLong(WPCConfigurationLong.RC_KEEP_MONITORING_DELAY) * 60 * 1000; // Add new recent changes to the list modelRC.addRecentChanges(newRC); // Remove old changes List<RecentChange> filteredNewRC = new ArrayList<RecentChange>(); for (RecentChange rc : newRC) { if (currentTime.getTime() < rc.getTimestamp().getTime() + delayForNew) { filteredNewRC.add(rc); } } // Check if an update has been made on a monitored page for (RecentChange rc : filteredNewRC) { if (monitoredPages.containsKey(rc.getTitle())) { Page page = DataManager.getPage(getWikipedia(), rc.getTitle(), null, null, null); try { updateDabWarning.updateWarning( Collections.singletonList(page), null, null, null); } catch (APIException e) { // Nothing to do } monitoredPages.put(rc.getTitle(), Long.valueOf(currentTime.getTime())); } } // Check monitored pages for expired delay Iterator<Entry<String, Long>> itPages = monitoredPages.entrySet().iterator(); while (itPages.hasNext()) { Entry<String, Long> entry = itPages.next(); if (currentTime.getTime() > entry.getValue().longValue() + delayMonitoring) { itPages.remove(); } } // Update list of interesting recent changes for (RecentChange rc : filteredNewRC) { if (isInterestingNamespace(rc)) { if (RecentChange.TYPE_NEW.equals(rc.getType())) { if (rc.isNew()) { modelRCInteresting.addRecentChange(rc); } } else if (RecentChange.TYPE_EDIT.equals(rc.getType())) { if (modelRCInteresting.containsRecentChange(rc.getTitle())) { modelRCInteresting.addRecentChange(rc); } } else if (RecentChange.TYPE_LOG.equals(rc.getType())) { if (RecentChange.LOG_TYPE_DELETE.equals(rc.getLogType()) && RecentChange.LOG_ACTION_DELETE_DELETE.equals(rc.getLogAction())) { modelRCInteresting.removeRecentChanges(rc.getTitle()); } } } } // Check if interesting recent changes are old enough List<RecentChange> interestingRC = modelRCInteresting.getRecentChanges(); List<Page> pages = new ArrayList<Page>(); Map<String, String> creators = new HashMap<String, String>(); Map<String, List<String>> modifiers = new HashMap<String, List<String>>(); while (!interestingRC.isEmpty()) { // Retrieve synthetic information about recent changes for one title List<RecentChange> listRC = extractRecentChanges(interestingRC); String title = listRC.get(0).getTitle(); String creator = null; List<String> pageModifiers = new ArrayList<String>(); boolean oldEnough = true; boolean redirect = false; for (int rcNum = listRC.size(); rcNum > 0; rcNum--) { RecentChange rc = listRC.get(rcNum - 1); if (currentTime.getTime() <= rc.getTimestamp().getTime() + delayForNew) { oldEnough = false; } String user = rc.getUser(); redirect = rc.isRedirect(); if (rc.isNew()) { creator = user; } else { if (!rc.isBot()) { if ((creator == null) || (!creator.equals(user))) { if (!pageModifiers.contains(user)) { pageModifiers.add(user); } } } } } if (oldEnough) { modelRCInteresting.removeRecentChanges(title); if (!redirect) { Page page = DataManager.getPage(getWikipedia(), title, null, null, null); pages.add(page); creators.put(title, creator); modifiers.put(title, pageModifiers); } } } // Update disambiguation warnings if (!pages.isEmpty()) { try { Stats stats = new Stats(); createDabWarning.updateWarning( pages, creators, modifiers, stats); List<Page> updatedPages = stats.getUpdatedPages(); if (updatedPages != null) { for (Page page : updatedPages) { monitoredPages.put(page.getTitle(), Long.valueOf(currentTime.getTime())); } } } catch (APIException e) { // Nothing to do } } } /** * Extract a list of recent changes for the same page. * * @param allRC List of recent changes. * @return List of recent changes for one page. */ private List<RecentChange> extractRecentChanges(List<RecentChange> allRC) { if ((allRC == null) || (allRC.isEmpty())) { return null; } List<RecentChange> result = new ArrayList<RecentChange>(); String title = allRC.get(0).getTitle(); Iterator<RecentChange> itRC = allRC.iterator(); while (itRC.hasNext()) { RecentChange rc = itRC.next(); if (Page.areSameTitle(title, rc.getTitle())) { result.add(rc); itRC.remove(); } } return result; } /** * @param rc Recent change. * @return True if the recent change is in an interesting namespace. */ private boolean isInterestingNamespace(RecentChange rc) { if (rc == null) { return false; } int namespace = rc.getNamespace(); if ((namespace == Namespace.MAIN) || (namespace == Namespace.TEMPLATE) || (namespace == Namespace.CATEGORY)) { return true; } return false; } }