예제 #1
0
    def DownloadConSeries(self, seriesname) -> bool:  # MainConSeriesFrame

        # Clear out any old information
        self._grid.Datasource = ConSeries()

        if seriesname is None or len(seriesname) == 0:
            # Nothing to load. Just return.
            return False

        if self._basedirectoryFTP is None:
            assert False  # Never take this branch.  Delete when I'm sure.

        ProgressMessage(self).Show("Loading " + self.Seriesname +
                                   "/index.html from fanac.org")
        file = FTP().GetFileAsString("/" + self.Seriesname, "index.html")

        pathname = self.Seriesname + "/index.html"
        if len(self._basedirectoryFTP) > 0:
            pathname = self._basedirectoryFTP + "/" + pathname

        if file is not None:

            # Get the JSON from the file
            j = FindBracketedText(file, "fanac-json")[0]
            if j is None or j == "":
                Log("DownloadConSeries: Can't load convention information from "
                    + pathname)
                wx.MessageBox("Can't load convention information from " +
                              pathname)
                return False

            try:
                self.FromJson(j)
            except (json.decoder.JSONDecodeError):
                Log("DownloadConSeries: JSONDecodeError when loading convention information from "
                    + pathname)
                wx.MessageBox(
                    "JSONDecodeError when loading convention information from "
                    + pathname)
                return False
        else:
            # Offer to download the data from Fancy 3
            self.Seriesname = seriesname
            resp = wx.MessageBox(
                "Do you wish to download the convention series " + seriesname +
                " from Fancyclopedia 3?", 'Shortcut',
                wx.YES | wx.NO | wx.ICON_QUESTION)
            if resp == wx.YES:
                self.DownloadConSeriesFromFancy(seriesname)

        if self.TextFancyURL is None or len(self.TextFancyURL) == 0:
            self.TextFancyURL = "fancyclopedia.org/" + WikiPagenameToWikiUrlname(
                seriesname)

        self._grid.MakeTextLinesEditable()
        self.RefreshWindow()
        ProgressMessage(self).Show(self.Seriesname + " Loaded",
                                   close=True,
                                   delay=0.5)
        return True
예제 #2
0
    def EditConInstancePage(self, instancename: str, irow: int) -> None:
        if len(instancename) == 0:
            dlg = wx.TextEntryDialog(
                None,
                "Please enter the name of the Convention Instance you wish to create.",
                "Enter Convention Instance name")
            if dlg.ShowModal() == wx.CANCEL or len(dlg.GetValue().strip(
            )) == 0:  # Do nothing if the user returns an empty string as name
                return
            instancename = dlg.GetValue()

        if irow >= self._grid.NumRows:
            self._grid.ExpandDataSourceToInclude(irow, 0)  # Add rows if needed

        with ModalDialogManager(ConInstanceDialogClass,
                                self._basedirectoryFTP + "/" + self.Seriesname,
                                self.Seriesname, instancename) as dlg:
            dlg.ConInstanceName = instancename

            # Construct a description of the convention from the information in the con series entry, if any.
            if irow < self._grid.Datasource.NumRows and len(
                    dlg.ConInstanceTopText.strip()) == 0:
                row = self._grid.Datasource.Rows[irow]
                dates = None
                if row.Dates is not None and not row.Dates.IsEmpty():
                    dates = str(row.Dates)
                locale = None
                if row.Locale is not None and len(row.Locale) > 0:
                    locale = row.Locale
                description = instancename
                if dates is not None and locale is not None:
                    description += " was held " + dates + " in " + locale + "."
                elif dates is not None:
                    description += " was held " + dates + "."
                elif locale is not None:
                    description += " was held in " + locale + "."
                if row.GoHs is not None and len(row.GoHs) > 0:
                    gohs = row.GoHs.replace("&amp;", "&")
                    if ("," in gohs and not ", jr" in gohs
                        ) or "&" in gohs or " and " in gohs:
                        description += "  The GoHs were " + gohs + "."
                    else:
                        description += "  The GoH was " + gohs + "."
                dlg.ConInstanceTopText = description

            dlg.ConInstanceName = instancename
            dlg.ConInstanceFancyURL = "fancyclopedia.org/" + WikiPagenameToWikiUrlname(
                instancename)

            dlg.MarkAsSaved()
            dlg.RefreshWindow()
            if dlg.ShowModal() == wx.ID_OK:
                if self._grid.Datasource.NumRows <= irow:
                    for i in range(irow - self._grid.Datasource.NumRows + 1):
                        self._grid.Datasource.Rows.append(Con())
                self._grid.Datasource.Rows[irow].Name = dlg.ConInstanceName
                self._grid.Datasource.Rows[irow].URL = dlg.ConInstanceName
                self.RefreshWindow()
예제 #3
0
 def ConTextConSeriesKeyUp(self, event):  # MainConSeriesFrame
     self.TextFancyURL = "fancyclopedia.org/" + WikiPagenameToWikiUrlname(
         self.tConSeries.GetValue())
예제 #4
0
    def FetchConSeriesFromFancy(self,
                                name,
                                retry: bool = False
                                ) -> bool:  # MainConSeriesFrame
        if name is None or name == "":
            return False

        wait = wx.BusyCursor(
        )  # The busy cursor will show until wait is destroyed
        pageurl = "https://fancyclopedia.org/" + WikiPagenameToWikiUrlname(
            name)
        try:
            response = urlopen(pageurl)
        except:
            del wait  # End the wait cursor
            Log("FetchConSeriesFromFancy: Got exception when trying to open " +
                pageurl)
            if not retry:
                dlg = wx.TextEntryDialog(
                    None,
                    "Load failed. Enter a different name and press OK to retry.",
                    "Try a different name?",
                    value=name)
                if dlg.ShowModal() == wx.CANCEL or len(
                        dlg.GetValue().strip()) == 0:
                    return False
                response = dlg.GetValue()
                return self.FetchConSeriesFromFancy(response)
            self._fancydownloadfailed = True
            return False

        html = response.read()
        soup = BeautifulSoup(html, 'html.parser')
        del wait  # End the wait cursor

        tables = soup.find_all("table", class_="wikitable mw-collapsible")
        if tables == None or len(tables) == 0:
            msg = "Can't find a table in Fancy 3 page " + pageurl + ".  Is it possible that its name on Fancy 3 is different?"
            Log(msg)
            self._fancydownloadfailed = True
            wx.MessageBox(msg)
            return False

        bsrows = tables[0].find_all("tr")
        headers = []
        rows = []
        for bsrow in bsrows:
            if len(headers) == 0:  #Save the header row separately
                heads = bsrow.find_all("th")
                if len(heads) > 0:
                    for head in heads:
                        headers.append(head.contents[0])
                    headers = [
                        RemoveAllHTMLTags(UnformatLinks(str(h))).strip()
                        for h in headers
                    ]
                    continue

            # Ordinary row
            cols = bsrow.find_all("td")
            row = []
            print("")
            if len(cols) > 0:
                for col in cols:
                    row.append(
                        RemoveAllHTMLTags(UnformatLinks(str(col))).strip())
            if len(row) > 0:
                rows.append(row)

        # Did we find anything?
        if len(headers) == 0 or len(rows) == 0:
            Log("FetchConSeriesFromFancy: Can't interpret Fancy 3 page '" +
                pageurl + "'")
            self._fancydownloadfailed = True
            wx.MessageBox("Can't interpret Fancy 3 page '" + pageurl + "'")
            return False

        # OK. We have the data.  Now fill in the ConSeries object
        # First, figure out which columns are which
        nname = FindIndexOfStringInList(headers, "Convention")
        if nname is None:
            nname = FindIndexOfStringInList(headers, "Name")
        ndate = FindIndexOfStringInList(headers, "Dates")
        if ndate is None:
            ndate = FindIndexOfStringInList(headers, "Date")

        nloc = FindIndexOfStringInList(headers, "Location")
        if nloc is None:
            nloc = FindIndexOfStringInList(headers, "Site, Location")
        if nloc is None:
            nloc = FindIndexOfStringInList(headers, "Site, City")
        if nloc is None:
            nloc = FindIndexOfStringInList(headers, "Site")

        ngoh = FindIndexOfStringInList(headers, "GoHs")
        if ngoh is None:
            ngoh = FindIndexOfStringInList(headers, "GoH")
        if ngoh is None:
            ngoh = FindIndexOfStringInList(headers, "Guests of Honor")
        if ngoh is None:
            ngoh = FindIndexOfStringInList(headers, "Guests")

        for row in rows:
            if len(row) != len(
                    headers
            ):  # Merged cells which usually signal a skipped convention.  Ignore them.
                continue
            con = Con()
            if nname is not None:
                con.Name = row[nname]
            if ndate is not None:
                con.Dates = FanzineDateRange().Match(row[ndate])
            if nloc is not None:
                con.Locale = row[nloc]
            if ngoh is not None:
                con.GoHs = row[ngoh]

            self._grid.Datasource.Rows.append(con)
        self.Seriesname = name
        self._fancydownloadfailed = False
        self.RefreshWindow()
        return True
예제 #5
0
    def UploadConSeries(self) -> bool:  # MainConSeriesFrame

        # First read in the template
        try:
            with open(PyiResourcePath("Template-ConSeries.html")) as f:
                file = f.read()
        except:
            wx.MessageBox("Can't read 'Template-ConSeries.html'")
            return False

        # Delete any trailing blank rows.  (Blank rows anywhere are as error, but we only silently drop trailing blank rows.)
        # Find the last non-blank row.
        last = None
        for i, row in enumerate(self._grid.Datasource.Rows):
            if len((row.GoHs + row.Locale + row.Name +
                    row.URL).strip()) > 0 or not row.Dates.IsEmpty:
                last = i
        # Delete the row or rows following it
        if last is not None and last < self._grid.Datasource.NumRows - 1:
            del self._grid.Datasource.Rows[last + 1:]

        # Determine if we're missing 100% of the data for the Dates, Location, or GoH columns so we can drop them from the listing

        ProgressMessage(self).Show("Uploading /" + self.Seriesname +
                                   "/index.html")

        # We want to do substitutions, replacing whatever is there now with the new data
        # The con's name is tagged with <fanac-instance>, the random text with "fanac-headertext"
        link = FormatLink(
            "https://fancyclopedia.org/" +
            WikiPagenameToWikiUrlname(self.Seriesname), self.Seriesname)
        file = SubstituteHTML(file, "title", self.Seriesname)
        file = SubstituteHTML(file, "fanac-instance", link)
        file = SubstituteHTML(file, "fanac-headertext", self.TextComments)

        showempty = self.m_radioBoxShowEmpty.GetSelection(
        ) == 0  # Radio button: Show Empty cons?
        hasdates = len([
            d.Dates for d in self._grid.Datasource.Rows
            if d.Dates is not None and not d.Dates.IsEmpty()
        ]) > 0
        haslocations = len([
            d.Locale for d in self._grid.Datasource.Rows
            if d.Locale is not None and len(d.Locale) > 0
        ]) > 0
        hasgohs = len([
            d.GoHs for d in self._grid.Datasource.Rows
            if d.GoHs is not None and len(d.GoHs) > 0
        ]) > 0

        # Now construct the table which we'll then substitute.
        newtable = '<table class="table" id="conseriestable">\n'
        newtable += "  <thead>\n"
        newtable += '    <tr id="conseriestable">\n'
        newtable += '      <th scope="col">Convention</th>\n'
        if hasdates:
            newtable += '      <th scope="col">Dates</th>\n'
        if haslocations:
            newtable += '      <th scope="col">Location</th>\n'
        if hasgohs:
            newtable += '      <th scope="col">GoHs</th>\n'
        newtable += '    </tr>\n'
        newtable += '  </thead>\n'
        newtable += '  <tbody>\n'
        for row in self._grid.Datasource.Rows:
            if (row.URL is None
                    or row.URL == "") and not showempty:  # Skip empty cons?
                continue
            newtable += "    <tr>\n"
            if row.URL is None or row.URL == "":
                newtable += '      <td>' + row.Name + '</td>\n'
            else:
                newtable += '      <td>' + FormatLink(
                    RemoveAccents(row.URL) + "/index.html",
                    row.Name) + '</td>\n'
            if hasdates:
                newtable += '      <td>' + str(
                    row.Dates) if not None else "" + '</td>\n'
            if haslocations:
                newtable += '      <td>' + row.Locale + '</td>\n'
            if hasgohs:
                newtable += '      <td>' + row.GoHs + '</td>\n'
            newtable += "    </tr>\n"
        newtable += "    </tbody>\n"
        newtable += "  </table>\n"

        file = SubstituteHTML(file, "fanac-table", newtable)
        file = SubstituteHTML(file, "fanac-json", self.ToJson())

        file = SubstituteHTML(
            file, "fanac-date",
            datetime.now().strftime("%A %B %d, %Y  %I:%M:%S %p") + " EST")

        # Now try to FTP the data up to fanac.org
        if self.Seriesname is None or len(self.Seriesname) == 0:
            Log("UploadConSeries: No series name provided")
            return False
        if not FTP().PutFileAsString(
                "/" + self.Seriesname, "index.html", file, create=True):
            wx.MessageBox("Upload failed")
            return False

        UpdateLog().LogText("Uploaded ConSeries: " + self.Seriesname)

        ProgressMessage(self).Show("Upload succeeded: /" + self.Seriesname +
                                   "/index.html",
                                   close=True,
                                   delay=0.5)
        self.MarkAsSaved(
        )  # It was just saved, so unless it's updated again, the dialog can exit without uploading
        self._uploaded = True  # Something's been uploaded
        self.RefreshWindow()
        return True
예제 #6
0
 def OnTextConInstanceNameKeyUp(self, event):
     self.ConInstanceFancyURL = "fancyclopedia.org/" + WikiPagenameToWikiUrlname(
         self.tConInstanceName.GetValue().strip())
     self.RefreshWindow()
예제 #7
0
    def OnUploadConInstancePage(self) -> None:

        # Delete any trailing blank rows.  (Blank rows anywhere are as error, but we only silently drop trailing blank rows.)
        # Find the last non-blank row.
        last = None
        for i, row in enumerate(self._grid.Datasource.Rows):
            if len((row.SourceFilename + row.SiteFilename + row.DisplayTitle +
                    row.Notes).strip()) > 0:
                last = i
        # Delete the row or rows following it
        if last is not None and last < self._grid.Datasource.NumRows - 1:
            del self._grid.Datasource.Rows[last + 1:]

        # Check to see if the data is valid
        error = False
        for i, row in enumerate(self._grid.Datasource.Rows):
            # Valid data requires
            #   If a text row, that some text exists
            #   If an external link row, that text and a properly formed URL exists (but does not check to see target exists)
            #   For a file, that there is an entry in the "Source File Name", "Site Name", and "Display Name" columns
            if row.IsText:
                if len((row.SourceFilename + row.SiteFilename +
                        row.DisplayTitle + row.Notes).strip()) == 0:
                    error = True
                    Log("Malformed text row: #" + str(i) + "  " + str(row))
                    for j in range(self._grid.NumCols):
                        self._grid.SetCellBackgroundColor(i, j, Color.Pink)
            elif row.IsLink:
                if len(row.URL.strip()) == 0 or len(
                        row.DisplayTitle.strip()) == 0:
                    error = True
                    Log("Malformed link row: #" + str(i) + "  " + str(row))
                    for j in range(self._grid.NumCols):
                        self._grid.SetCellBackgroundColor(i, j, Color.Pink)
            else:
                if len(row.SourceFilename.strip()) == 0 or len(
                        row.SiteFilename.strip()) == 0 or len(
                            row.DisplayTitle.strip()) == 0:
                    error = True
                    Log("Malformed file row: #" + str(i) + "  " + str(row))
                    for j in range(self._grid.NumCols):
                        self._grid.SetCellBackgroundColor(i, j, Color.Pink)
        if error:
            self._grid.Grid.ForceRefresh()
            wx.MessageBox("Malformed row found")
            return

        # Read in the template
        file = None
        try:
            Log("sys.path[0]=  " + sys.path[0])
            Log("sys.argv[0]=  " + sys.argv[0])
            Log("os.path.join(sys.path[0], 'Template-ConPage.html')=  " +
                os.path.join(sys.path[0], "Template-ConPage.html"))
            with open(PyiResourcePath("Template-ConPage.html")) as f:
                file = f.read()
        except:
            wx.MessageBox("Can't read 'Template-ConPage.html'")
            Log("Can't read 'Template-ConPage.html'")
            return

        ProgressMessage(self).Show("Uploading /" + self._seriesname + "/" +
                                   self._coninstancename + "/index.html")

        # We want to do substitutions, replacing whatever is there now with the new data
        # The con's name is tagged with <fanac-instance>, the random text with "fanac-headertext"
        fancylink = FormatLink(
            "https://fancyclopedia.org/" +
            WikiPagenameToWikiUrlname(self.ConInstanceName),
            self.ConInstanceName)
        file = SubstituteHTML(file, "title", self.ConInstanceName)
        file = SubstituteHTML(file, "fanac-instance", fancylink)
        file = SubstituteHTML(file, "fanac-stuff", self.ConInstanceTopText)

        # Fill in the top buttons
        s="<button onclick=\"window.location.href='https://fancyclopedia.org/"+WikiPagenameToWikiUrlname(self.ConInstanceName)+"'\"> Fancyclopedia 3 </button>&nbsp;&nbsp;"+ \
        "<button onclick=\"window.location.href='..'\">All "+self._seriesname+"s</button>"
        file = SubstituteHTML(file, "fanac-topbuttons", s)

        # If there are missing page counts for pdfs, try to gett hem. (This can eventually be eliminated as there will be no pre-V7 files on the server.)
        self.FillInMissingPDFPageCounts()

        file = SubstituteHTML(file, "fanac-json", self.ToJson())
        file = SubstituteHTML(
            file, "fanac-date",
            datetime.now().strftime("%A %B %d, %Y  %I:%M:%S %p") + " EST")
        if len(self.Credits.strip()) > 0:
            file = SubstituteHTML(
                file, "fanac-credits", 'Credits: ' + self.Credits.strip() +
                "<br>")  #<p id="randomtext"><small>   +'</small></p>'

        def FormatSizes(row) -> str:
            info = ""
            if row.Size > 0 or (row.Pages is not None and row.Pages > 0):
                info = "<small>("
                if row.Size > 0:
                    info += "{:,.1f}".format(row.Size / (1024**2)) + '&nbsp;MB'
                if row.Pages is not None and row.Pages > 0:
                    if row.Size > 0:
                        info += "; "
                    info += str(row.Pages) + " pp"
                info += ")</small>"
            return info

        showExtensions = self.radioBoxShowExtensions.GetSelection() != 0

        def MaybeSuppressPDFExtension(fn: str, suppress: bool) -> str:
            if suppress:
                parts = os.path.splitext(row.DisplayTitle)
                if parts[1].lower() in [
                        ".pdf", ".jpg", ".png", ".doc", ".docx"
                ]:
                    fn = parts[0]
            return fn

        if self.radioBoxFileListFormat.GetSelection(
        ) == 0:  # Are we to output a table?
            # Now construct the table which we'll then substitute.
            newtable = '<table class="table"  id="conpagetable">\n'
            newtable += "  <thead>\n"
            newtable += "    <tr>\n"
            newtable += '      <th scope="col">Document</th>\n'
            newtable += '      <th scope="col">Size</th>\n'
            newtable += '      <th scope="col">Notes</th>\n'
            newtable += '    </tr>\n'
            newtable += '  </thead>\n'
            newtable += '  <tbody>\n'
            for i, row in enumerate(self._grid.Datasource.Rows):
                newtable += "    <tr>\n"
                # Display title column
                if row.IsText:
                    newtable += '      <td colspan="3">' + row.SourceFilename + " " + row.SiteFilename + " " + row.DisplayTitle + " " + row.Notes + '</td>\n'
                elif row.IsLink:
                    newtable += '      <td colspan="3">' + FormatLink(
                        row.URL, row.DisplayTitle) + '</td>\n'
                else:
                    # The document title/link column
                    s = MaybeSuppressPDFExtension(row.DisplayTitle,
                                                  showExtensions)
                    newtable += '      <td>' + FormatLink(row.SiteFilename,
                                                          s) + '</td>\n'

                    # This is the size & page count column
                    newtable += '      <td>' + FormatSizes(row) + '</td>\n'

                    # Notes column
                    info = '      <td> </td>\n'
                    if len(row.Notes) > 0:
                        info = '      <td>' + str(row.Notes) + '</td>\n'
                    newtable += info

                newtable += "    </tr>\n"
            newtable += "    </tbody>\n"
            newtable += "  </table>\n"
        else:  # Output a list
            # Construct a list which we'll then substitute.
            newtable = '<ul  id="conpagetable">\n'
            for row in self._grid.Datasource.Rows:
                if row.IsText:
                    text = row.SourceFilename + " " + row.SiteFilename + " " + row.DisplayTitle + " " + row.Notes
                    newtable += '    </ul><b>' + text.strip(
                    ) + '</b><ul id="conpagetable">\n'
                elif row.IsLink:
                    newtable += '    <li id="conpagetable">' + FormatLink(
                        row.URL, row.DisplayTitle) + "</li>\n"
                else:
                    s = MaybeSuppressPDFExtension(row.DisplayTitle,
                                                  showExtensions)
                    newtable += '    <li id="conpagetable">' + FormatLink(
                        row.SiteFilename, s)

                    val = FormatSizes(row)
                    if len(val) > 0:
                        newtable += '&nbsp;&nbsp;' + val + '\n'
                    else:
                        newtable += '&nbsp;&nbsp;(--)\n'

                    # Notes
                    if len(row.Notes) > 0:
                        newtable += "&nbsp;&nbsp;(" + str(row.Notes) + ")"
                    newtable += "</li>\n"

            newtable += "  </ul>\n"

        file = SubstituteHTML(file, "fanac-table", newtable)

        if not FTP().PutFileAsString(
                "/" + self._seriesname + "/" + self._coninstancename,
                "index.html",
                file,
                create=True):
            Log("Upload failed: /" + self._seriesname + "/" +
                self._coninstancename + "/index.html")
            wx.MessageBox("OnUploadConInstancePage: Upload failed: /" +
                          self._seriesname + "/" + self._coninstancename +
                          "/index.html")
            ProgressMessage(self).Close()
            return

        wd = "/" + self._seriesname + "/" + self._coninstancename
        FTP().CWD(wd)
        for delta in self.conInstanceDeltaTracker.Deltas:
            if delta.Verb == "add":
                ProgressMessage(self).Show("Adding " +
                                           delta.Con.SourcePathname + " as " +
                                           delta.Con.SiteFilename)
                Log("delta-ADD: " + delta.Con.SourcePathname + " as " +
                    delta.Con.SiteFilename)
                FTP().PutFile(delta.Con.SourcePathname, delta.Con.SiteFilename)
            elif delta.Verb == "rename":
                ProgressMessage(self).Show("Renaming " + delta.Oldname +
                                           " to " + delta.Con.SiteFilename)
                Log("delta-RENAME: " + delta.Oldname + " to " +
                    delta.Con.SiteFilename)
                FTP().Rename(delta.Oldname, delta.Con.SiteFilename)
            elif delta.Verb == "delete":
                if not delta.Con.IsText and not delta.Con.IsLink:
                    ProgressMessage(self).Show("Deleting " +
                                               delta.Con.SiteFilename)
                    Log("delta-DELETE: " + delta.Con.SiteFilename)
                    if len(delta.Con.SiteFilename.strip()) > 0:
                        FTP().DeleteFile(delta.Con.SiteFilename)
            elif delta.Verb == "replace":
                ProgressMessage(self).Show("Replacing " + delta.Oldname +
                                           " with new/updated file")
                Log("delta-REPLACE: " + delta.Con.SourcePathname + " <-- " +
                    delta.Oldname)
                Log("   delta-DELETE: " + delta.Con.SiteFilename)
                if len(delta.Con.SiteFilename.strip()) > 0:
                    FTP().DeleteFile(delta.Con.SiteFilename)
                Log("   delta-ADD: " + delta.Con.SourcePathname + " as " +
                    delta.Con.SiteFilename)
                FTP().PutFile(delta.Con.SourcePathname, delta.Con.SiteFilename)
            else:
                Log("delta-UNRECOGNIZED: " + str(delta))

        UpdateLog().Log(self._seriesname, self._coninstancename,
                        self.conInstanceDeltaTracker)

        self.conInstanceDeltaTracker = ConInstanceDeltaTracker(
        )  # The upload is complete. Start tracking changes afresh

        ProgressMessage(self).Show("Upload succeeded: /" + self._seriesname +
                                   "/" + self._coninstancename + "/index.html",
                                   close=True,
                                   delay=0.5)
        self.MarkAsSaved()
        self.Uploaded = True
        self.RefreshWindow()