예제 #1
0
 def accept(self):
     # Override accept so we can first validate
     if self.is_valid():
         category_name = self.addEditCatLineEdit.text().strip()
         try:
             with session_scope() as session:
                 if self.edit_cat_id is not None:
                     category = session.query(Category).get(
                         int(self.edit_cat_id))
                     category.category_name = category_name
                     logger.debug('Edited cat with id %s' %
                                  self.edit_cat_id)
                 else:
                     category = Category(category_name=category_name)
                     session.add(category)
                     logger.debug('Added cat with name %s' % category_name)
         except exc.IntegrityError as int_exc:
             logger.debug(int_exc)
             QMessageBox.warning(self, "Already exists warning",
                                 unicode('This category already exists'))
             self.addEditCatLineEdit.setFocus()
             self.selectAll()
             return
         except Exception as uexc:
             logger.error(str(uexc))
             QMessageBox.warning(self, "Unexpected Error",
                                 unicode('Could not edit category.'))
             return
         else:
             # All good, accept after triggering tree refresh with sig
             self.categories_changed.emit()
             QDialog.accept(self)
예제 #2
0
    def init_list(self):
        '''
        Build list widget according to db
        '''
        # Init
        all_categories = []
        selected_categories = []

        try:
            with session_scope() as session:
                all_categories = session.query(Category.category_name).order_by(Category.category_name).all()
                all_categories = [c[0] for c in all_categories]
                if self.edit_reminder_id is not None:
                    reminder = session.query(Reminder).get(int(self.edit_reminder_id))
                    selected_categories = [category.category_name for category in reminder.categories]
        except Exception as cat_exc:
            QtGui.QMessageBox.warning(self, "Unexpected error", unicode('Could not init categories list'))
            logger.error(str(cat_exc))
        else:
            logger.debug('All categories is %s' % all_categories)
            logger.debug('Selected categories is %s' % selected_categories)
            for category in all_categories:
                # Add to list widget
                item = QtGui.QListWidgetItem(category, self.manageRemCatsListWidget)
                if category in selected_categories:
                    item.setSelected(True)

        # Ensure ordered
        self.manageRemCatsListWidget.sortItems()
예제 #3
0
	def areadCSVLinks(self):
		'''read input CSV file. File MUST be structured either: preferred = *kwargs,Name,Link || optional = *kwargs,Link'''
		seasons = self.seasons
		with open(self.csvinput, 'r', newline='', encoding='latin-1') as csvread:
			reader = csv.reader(csvread)
			playerdict = {} # define a basic dict to pass csv information into
			i = 0
			for row in reader:
				if i < 1: # define headers
					self.header = [str(i+1) for i in range(len(row))] # handle kwargs as header - assign number
					self.header[-2] = "Name"
					self.header[-1] = "Link"
				name,link = row[-2:] # select last two items
				try:
					gamertag = link.split('/')[-1] # last item in link is gamertag
					platform = link.split('/')[-2] # item before gamertag is platform
				except IndexError:
					logger.error("Gamertag:%(name)s Link:%(link)s is not formatted properly" % locals())
				else:
					playerdict[gamertag] = {} # define dict for each gamertag and values for that gamertag
					a = 0
					for item in row:  # handle kwargs
						if len(row) - a > 2:
							playerdict[gamertag][a] = item
							a += 1
					playerdict[gamertag]['platform'] = platform
					playerdict[gamertag]['name'] = name
					playerdict[gamertag]['link'] = link
					i += 1
		return playerdict
예제 #4
0
    def refresh_tree(self):
        '''
        Re-build the tree from database.

        Record the current category item, and if it is still present
        after the re-build set it as the current, else set 'Upcoming' category as current.

        Note that the root is the "Categories" item, the first 3 of its children
        are static, mandatory categories, "Upcoming", "Complete" and "Uncategorized"
        that don't come from the database - they are fixed. We only delete
        the custom user categories that come after these...
        '''
        # Record the current category
        old_category = self.mainTreeWidget.currentItem()
        if old_category is not None:
            old_category = old_category.text(
                self.mainTreeWidget.currentColumn())

        # Reload all the categories from the database
        categories = []
        try:
            with session_scope() as session:
                categories = session.query(Category.category_name).order_by(
                    Category.category_name).all()
                categories = [c[0] for c in categories]
        except Exception as uexc:
            logger.error(str(uexc))
            QtGui.QMessageBox.error(self, 'Unexpected error',
                                    'Could not select query categories.')
            return
        logger.debug('All categories %s' % categories)

        # Rebuild the tree widget with the reloaded categories
        root = self.mainTreeWidget.topLevelItem(0)
        root.setExpanded(True)
        # Clear custom categories, reverse important to not mess up interator
        # First 3 kids are static, don't delete.
        for i in reversed(range(root.childCount())):
            if i > 2:
                root.removeChild(root.child(i))

        # Set 'Upcoming' selected by default but set old current cat as current if
        # it still exists
        all_item = root.child(0)
        self.mainTreeWidget.setCurrentItem(all_item)
        for category in categories:
            cat_child = QtGui.QTreeWidgetItem()
            cat_child.setText(0, category)
            # Icon
            cat_icon = QtGui.QIcon()
            cat_icon.addPixmap(QtGui.QPixmap(":/icons/icons/play-button.png"),
                               QtGui.QIcon.Normal, QtGui.QIcon.Off)
            cat_child.setIcon(0, cat_icon)
            root.addChild(cat_child)
            if category == old_category:
                logger.debug('Set %s as current category item' % category)
                self.mainTreeWidget.setCurrentItem(cat_child)
예제 #5
0
    def export_action_triggered(self):
        """Database export handler"""

        # Build JSON
        jdump = {
            'timestamp': arrow.utcnow().timestamp,
            'reminders': [],
            'categories': []
        }
        try:
            with session_scope() as session:
                for r in session.query(Reminder).all():
                    rdict = r.as_dict()
                    rdict['category_ids'] = [
                        c.category_id for c in r.categories
                    ]
                    jdump['reminders'].append(rdict)
                jdump['categories'] = [
                    c.as_dict() for c in session.query(Category).all()
                ]
        except Exception as xp_exc:
            logger.error(str(xp_exc))
            QtGui.QMessageBox(self, "Unexpected Exception",
                              "Could not export to file")
            return

        dbFile = QtGui.QFileDialog.getSaveFileName(
            parent=None,
            caption="Export database to a file",
            directory=".",
            filter="QTierna JSON (*.json)")
        if dbFile[0]:
            try:
                with io.open(dbFile[0], 'w', encoding='utf-8') as f:
                    logger.debug(jdump)
                    f.write(json.dumps(jdump, ensure_ascii=False))
                    msg = (
                        "Successfully exported %i reminders and %i categories to a file\r\n%s"
                        % (len(jdump['reminders']), len(jdump['categories']),
                           (QtCore.QDir.toNativeSeparators(dbFile[0]))))
                    QtGui.QMessageBox.information(self, __appname__, msg)
            except Exception as xportexc:
                QtGui.QMessageBox.critical(
                    self, __appname__,
                    "Error exporting file, error is\r\n" + str(xportexc))
                return
예제 #6
0
    def readCSVLinks(self):
        '''read input CSV file. File MUST be structured either: Link || *kwargs,Link || *kwargs,Name,Link'''
        scrape = Webscrape()
        seasons = self.seasons

        with open(self.csvinput, newline='', encoding="ISO-8859-1") as csvread:
            reader = csv.reader(csvread)
            i = 0
            for row in reader:
                if i < 1:
                    self.header = [str(i + 1) for i in range(len(row))]
                    self.header[-2] = "ID"
                    self.header[-1] = "Link"
                    self.rows.append(row)
                    i += 1
                else:
                    self.rows.append(row)
                    i += 1
        row_count = sum(1 for row in self.rows)
        i = 1
        for row in tqdm(self.rows, total=row_count):
            name, link = row[-2:]  # select last two items
            try:
                gamertag = link.split('/')[-1]  # last item in link is gamertag
                platform = link.split('/')[
                    -2]  # item before gamertag is platform
            except IndexError:
                logger.error(
                    "Gamertag:%(name)s Link:%(link)s is not formatted properly"
                    % locals())
                i += 1
            else:
                newrow = []
                data = scrape.retrieveDataRLTracker(gamertag=gamertag,
                                                    platform=platform,
                                                    seasons=seasons,
                                                    tiertf=self.tiertf)
                newrow = self._dictToList(data)
                a = 0
                for item in row:  #account for kwargs
                    newrow.insert(a, item)
                    a += 1
                self.newrows.append(newrow)
                i += 1
        self.writeCSV()
예제 #7
0
    def _testSeason(self, season):
        '''True/False to see if season is valid
		1) is a number, 2) is a valid season less than current number, 3) if current season in list, just pass'''
        latestseason = self.latestseason
        try:
            if not season.isdigit():
                raise NameError
            if int(season) > int(latestseason):
                raise ValueError
            if int(season) == int(latestseason):
                return False
        except ValueError:
            logger.error(
                "Season:%(season)s was higher than Current Season:%(latestseason)s"
                % locals())
            return False
        except NameError:
            logger.error("Season:%(season)s was not a number" % locals())
            return False
        else:
            return True
예제 #8
0
 def accept(self, edit=False):
     '''
     Save the new/edited Reminder if valid
     '''
     if self.is_valid():
         try:
             category_names = []
             if self.categoriesDlg is not None:
                 # User edited categories we must update
                 category_names = self.categoriesDlg._get_selected_categories(
                 )
             logger.debug('Selected categories were %s' % category_names)
             with session_scope() as session:
                 category_instances = []
                 if category_names:
                     category_instances = session.query(Category).filter(
                         Category.category_name.in_(category_names)).all()
                 if self.edit_reminder_id:
                     reminder = session.query(Reminder).get(
                         int(self.edit_reminder_id))
                 else:
                     reminder = Reminder()
                 reminder.due = self._get_reminder_utc_datetime_str()
                 reminder.complete = self._()
                 reminder.note = self._get_reminder_note()
                 if category_instances:
                     reminder.categories = category_instances
                 session.add(reminder)
         except exc.IntegrityError as int_exc:
             # Rollback already handled by scoped_session ctx manager
             logger.error(int_exc)
             QtGui.QMessageBox.warning(
                 self, "Already exists warning",
                 unicode('This reminder already exists'))
             return
         else:
             # All good, accept
             QtGui.QDialog.accept(self)
예제 #9
0
    def __init__(self, edit_cat_id=None, parent=None):
        super(AddEditCatDialog, self).__init__(parent)
        self.setupUi(self)

        # If editing
        self.edit_cat_id = edit_cat_id
        if self.edit_cat_id:
            try:
                with session_scope() as session:
                    category = session.query(Category).get(
                        int(self.edit_cat_id))
                    self.addEditCatLineEdit.setText(category.category_name)
            except Exception as cexc:
                logger.error(str(cexc))
                QMessageBox.warning(self, "Unexpected Error",
                                    unicode('Could not set category name.'))
                return

        # Preventitive validation
        self.addEditCatLineEdit.setMaxLength(50)
        self.addEditCatButtonBox.button(
            QDialogButtonBox.Save).setEnabled(False)
        self.addEditCatLineEdit.textChanged.connect(self.disableAddButton)
예제 #10
0
    def delete_cats_btn_pressed(self):
        '''
        Delete selected categories. Preventitive validation (enable/disable delete button)
        means always some categories selected if made it here
        '''
        # Get all checked in listviewwidget
        # delete from db
        selected_items = self.manageRemCatsListWidget.selectedItems()
        selected_category_names = [item.text() for item in selected_items]
        logger.debug('Got %i categories for deletion..' % len(selected_category_names))
        try:
            with session_scope() as session:
                session.query(Category).filter(Category.category_name.in_(selected_category_names)).delete(synchronize_session='fetch')
        except Exception as del_exc:
            QtGui.QMessageBox.warning(self, "Unexpected error", unicode('Could not delete categories'))
            logger.error(str(del_exc))
        else:
            logger.debug('Deleted %s' % selected_category_names)
            self.categories_changed.emit()

            # Delete these items from the list widget
            for selected_item in selected_items:
                logger.debug('Removing item %s from list widget' % selected_item)
                self.manageRemCatsListWidget.takeItem(self.manageRemCatsListWidget.row(selected_item))
예제 #11
0
    def launch_notification(self, reminder_id):
        due = note = None
        try:
            with session_scope() as session:
                reminder = session.query(Reminder).get(int(reminder_id))
                due = reminder.due
                note = reminder.note
                reminder.complete = True
        except Exception as uexc:
            logger.error(str(uexc))
            QtGui.QMessageBox(self, 'Unexpected exception',
                              'Could not mark due reminder as complete')
            return

        # Get local datetime for output to user and format note as html
        local_due = dt2str(utcstr2local(due,
                                        self.time_zone,
                                        date_format='%Y-%m-%d %H:%M'),
                           date_format='%d %b %I:%M%p')
        htmlcontent = '<p>%s</p>' % note

        # QApplication.instance().beep()
        # if QtGui.QSound.isAvailable():
        #     # Seems I would have to recompile with NAS support, but
        #     # what does that mean for python when pyside was pip installed??
        #     QtGui.QSound.play("media/alarm_beep.wav")
        media = Phonon.MediaObject()
        audio = Phonon.AudioOutput(Phonon.MusicCategory)
        Phonon.createPath(media, audio)
        # alarm_file = os.path.join(os.getcwd(), 'media/alarm_beep.wav')
        alarm_file = resource_path('alarm_beep.wav')
        logger.debug('Trying to open alarm file...%s' % alarm_file)
        f = QtCore.QFile(alarm_file)
        if f.exists():
            source = Phonon.MediaSource(alarm_file)
            if source.type() != -1:  # -1 stands for invalid file
                media.setCurrentSource(source)
                media.play()
        else:
            logger.debug('Alert media missing: %s' % alarm_file)

        # Systray notification
        self.tray_icon.showMessage(unicode('Reminder due at %s' % local_due),
                                   smart_truncate(note, length=100),
                                   QtGui.QSystemTrayIcon.Information, 5000)

        # Dialog notification
        self.show()
        dlg = NotificationDialog()
        dlg.notificationTextBrowser.setHtml(htmlcontent)
        dlg.dtLabel.setText(local_due)
        dlg.setWindowTitle(unicode('Due at %s' % local_due))
        # Change std buttons to "Reschedule" and "Mark Complete".
        # Resched will set complete=False and launch the edit reminder with
        # time selected. "Mark Complete" does nothing, since we already
        # marked complete to prevent further popups
        dlg.notificationButtonBox.button(
            QtGui.QDialogButtonBox.Ok).setText('Mark Complete')
        dlg.notificationButtonBox.button(
            QtGui.QDialogButtonBox.Cancel).setText('Reschedule')
        if dlg.exec_():
            logger.debug(
                'User wants to close dlg and keep the reminder as completed')
        else:
            # Launch edit reminder
            logger.debug('User wants to reschedule')
            self.addedit_rem_action_triggered(reminder_id=reminder_id)

        # Refresh table to account for this reminder completion
        self.refresh_table()
예제 #12
0
 def retrieveDataRLTracker(self,
                           gamertag="memlo",
                           platform="steam",
                           seasons=["12"],
                           tiertf=False):
     '''Python BeautifulSoup4 Webscraper to https://rocketleague.tracker.network/ to retrieve gamer data'''
     playlistdict = {
         0: 'Un-Ranked',
         10: 'Ranked Duel 1v1',
         11: 'Ranked Doubles 2v2',
         12: 'Ranked Solo Standard 3v3',
         13: 'Ranked Standard 3v3',
         27: 'Hoops',
         28: 'Rumble',
         29: 'Dropshot',
         30: 'Snowday'
     }
     latestseason = self.latestseason
     webpathmmr = self.webpathmmr
     webpath = self.webpath
     rltrackermissing = self.rltrackermissing
     psyonixdisabled = self.psyonixdisabled
     playerdata = {}  # define the playerdata dict
     playerdata[gamertag] = {}  # define the gamertag dict
     if '[]' in seasons:
         logger.warning(
             "Season was not set - you should never see this error")
     page = requests.get("%(webpath)s/%(platform)s/%(gamertag)s" % locals())
     if page.status_code == 200:
         soup = BeautifulSoup(page.content, features="lxml")
         if soup(text=re.compile(rltrackermissing)
                 ):  # find "we could not find your stats" on webpage
             logger.critical(
                 "Player Missing - URL:%(webpath)s/%(platform)s/%(gamertag)s"
                 % locals())
         elif soup(
                 text=re.compile(psyonixdisabled)
         ):  # find "Psyonix has disabled the Rocket League API" on webpage
             logger.critical(
                 "Psyonix Disabled API - URL:%(webpath)s/%(platform)s/%(gamertag)s"
                 % locals())
         else:
             if latestseason in seasons:
                 playerdata[gamertag][latestseason] = {
                 }  #define the season dict
                 pagemmr = requests.get(
                     "%(webpathmmr)s/%(platform)s/%(gamertag)s" % locals())
                 if pagemmr.status_code == 200:
                     soupmmr = BeautifulSoup(pagemmr.content,
                                             features="lxml")
                     # for every playlist, create a record of data
                     # on the website, grab the 'div' for specific playlist
                     for numrank, playlist in playlistdict.items():
                         try:
                             soupmmr.find('a', {
                                 "data-id": numrank
                             }).find('span').text
                         except:
                             playerdata[gamertag][latestseason][
                                 playlist] = None
                         else:
                             playerdata[gamertag][latestseason][
                                 playlist] = {}  #define the playlist dict
                             playerdata[gamertag][latestseason][playlist][
                                 'MMR'] = soupmmr.find(
                                     'a', {
                                         "data-id": numrank
                                     }).find('span').text
                             playerdata[gamertag][latestseason][playlist][
                                 'Games Played'] = soupmmr.find(
                                     'div', {
                                         "data-id": numrank
                                     }).find('div').find('span').text
                             playerdata[gamertag][latestseason][playlist][
                                 'Rank'] = soupmmr.find(
                                     'div', {
                                         "data-id": numrank
                                     }).select('div > span')[2].text
                             playerdata[gamertag][latestseason][playlist][
                                 'Rank Division'] = soupmmr.find(
                                     'div', {
                                         "data-id": numrank
                                     }).select('div > h4')[2].text
                     if tiertf == True:
                         try:
                             scripttags = soupmmr.findAll(
                                 'script', type='text/javascript'
                             )  #grab all <script> tags
                         except:
                             logger.error(
                                 "Finding <script/> tags in website:%(webpathmmr)s/%(platform)s/%(gamertag)s"
                                 % locals())
                         else:
                             scripttags = soupmmr.findAll(
                                 'script', type='text/javascript'
                             )  #grab all <script> tags
                             for script in scripttags:  #find the data we care about
                                 if 'var data' in script.text:
                                     a = script.text.replace(
                                         ' ', '').replace('\n', '').replace(
                                             '\r', '').split(';')
                                     data = {}
                                     for blob in a[1:6]:
                                         b = re.split('\w+.:', blob)
                                         if '[]' in b[
                                                 2] and 'Un-Ranked' not in b[
                                                     2]:  #if there aren't any dates listed, except in Un-Ranked
                                             #logger.error("Issue for player:%s in season:%s with dates:%s and tier:%s in playlist:%s" % (gamertag,latestseason,b[2],b[4],b[1])) # using locals with dict[k] doesn't work :/
                                             continue
                                         else:
                                             if 'Un-Ranked' in b[1]:
                                                 playlist = 'Un-Ranked'
                                             elif 'RankedDuel1v1' in b[1]:
                                                 playlist = 'Ranked Duel 1v1'
                                             elif 'RankedDoubles2v2' in b[
                                                     1]:
                                                 playlist = 'Ranked Doubles 2v2'
                                             elif 'RankedSoloStandard3v3' in b[
                                                     1]:
                                                 playlist = 'Ranked Solo Standard 3v3'
                                             elif 'RankedStandard3v3' in b[
                                                     1]:
                                                 playlist = 'Ranked Standard 3v3'
                                             else:
                                                 playlist = ''
                                             #dates = list(filter(None, re.split('\[|,|\]|\'',b[2]))) #futureproof
                                             #rating_over_time' = list(filter(None, re.split('\[|,|\]',b[3]))) #futureproof
                                             tier_over_time = list(
                                                 filter(
                                                     None,
                                                     re.split(
                                                         '\[|,|\]|\}',
                                                         b[4])))
                                             if tier_over_time is not None:
                                                 playerdata[gamertag][
                                                     latestseason][playlist][
                                                         'Tier'] = tier_over_time[
                                                             -1]
                                             else:
                                                 playerdata[gamertag][
                                                     latestseason][
                                                         playlist][
                                                             'Tier'] = None
             if '[]' not in seasons:
                 for season in seasons:
                     if self._testSeason(season):
                         playerdata[gamertag][season] = {
                         }  #define the season dict
                         seasonid = "season-%(season)s" % locals()
                         try:
                             seasontable = soup.find(id=seasonid).select(
                                 'table > tbody')[0].select('tr')[1:]
                         except:
                             logger.error(
                                 "Finding season:%(season)s data for gamertag:%(gamertag)s"
                                 % locals())
                         else:
                             seasontable = soup.find(id=seasonid).select(
                                 'table > tbody')[0].select('tr')
                             playerdata[gamertag][
                                 season]  #define the playlist dict
                             for tabledata in seasontable:
                                 td = tabledata.find_all('td')
                                 listdata = []
                                 for x in td:
                                     data = x.text.strip().split('\n')
                                     listdata = listdata + data
                                 try:
                                     blank1, playlist, blank2, writtenrank, mmr, gamesplayed = listdata
                                 except ValueError:
                                     logger.error(
                                         "Error parsing:%(listdata)s season:%(season)s data for gamertag:%(gamertag)s"
                                         % locals())
                                 else:
                                     blank1, playlist, blank2, writtenrank, mmr, gamesplayed = listdata
                                     playerdata[gamertag][season][
                                         playlist] = {
                                         }  #define the playlist dict
                                     playerdata[gamertag][season][playlist][
                                         'MMR'] = mmr
                                     playerdata[gamertag][season][playlist][
                                         'Games Played'] = gamesplayed
     #if self.playerdata.get(gamertag) != {}:
     return playerdata