Panorama X file corruption

I have what for me is a large database: 1.8 MB on disk, 18345 records. It has no forms at all. It’s just the data sheet and a couple of procedures.

A few days ago, as I was working on it, adding records, Panorama X crashed and quit. I reopened the database and saw that Panorama couldn’t find any of the records I had added that morning. Then Panorama X crashed again and after that, I was unable to open the database anymore.

Restarting the Mac didn’t help, neither did the transfer of the file to a different Mac. I didn’t try anything else but just started over with the last version on Time Machine. Unfortunately, that was the previous day’s last backup as Time Machine had done nothing during the two hours I had worked on the file before it crashed. Sometimes it backs up after less than an hour, sometimes it waits for over two hours. There doesn’t seem to be any way to get it to be more regular, other than manually telling it to “Back Up Now”.

I came here to see if other people had had the same experience and so I tried to open the corrupted database using the Open Data Sheet Only pop-up menu, but it didn’t open either. Anyway, as I mentioned, my database is really nothing more than the data sheet anyway.

Now, I’m saving a copy by hand after about every hour of work.

Does anyone have suggestions about the cause of this corruption? And, more importantly, how to avoid it in the future, other than the repeated saving of a copy of the file while working on it?

1 Like

I have had Panorama X crash often, and the changes are not saved. So every time I do something I want to be sure is saved, I save it. I do not know when Panorama decides to autosave, which may be something set up by Apple, not Jim, so saving often just has to be done.

I forgot to mention that I have an old school sort of tic: I press cmd-s after just about every line I type. I don’t even think about it, it’s just an ingrained reflex, from the days when the word “autosave” didn’t exist. So, yes, the file had been “saved” many times when Panorama X crashed the first time. Now, it’s not just cmd-s, but I close the file, create a copy with the Finder to which I add the date and time and then I reopen the file to go on working.

If you are using a recent macOS, that should give you the ability to return to every saved version, even when you have no TimeMachine backup drive attached. See the menu item File > Revert To > Browse All versions …

I’m using MacOS Sierra 10.12.6. The method you suggest implies that the file can be opened. Without an open database there is no such menu item. Here, the corrupted file won’t open.

In this case I would create a new file with the same name and then try to restore previous versions.

Here is something you might consider adding to simplify things. Add this hotkey code to your .Initialize procedure (or make an .Initialize procedure if you don’t already have one):

definehotkeys “database”,“control-B”,{makefolder dbfolder()+info(“databasename”)+" Backup"
saveacopyas info(“databasename”)+" Backup/“+info(“databasename”)+” "+superdatepattern(supernow(),"MM\DD\YY @ ",“hh;mm am/pm”)}

This will allow you to press control-B and save a copy of your current database to a backup folder in the same folder as your database. This will not change the name of the currently open database unlike saveas would do. So, if your current database name is MyFile this will create a folder named “MyFile Backup” and save a copy of this database as “MyFile 04\07\18 @ 11;32 AM.pandb”.

Somethings to note here. You will probably wonder if the makefolder statement will cause problems if it already exists - no, it will just be ignored and have no effect. Next, notice that I have used reverse slashes to separate the date instead of the forward slash and a semicolon instead of a colon for the time. This is done because using a forward slash or a colon in a file name is not allowed by the finder.

As a side note I was going to make the scope of this hotkey global but it would not work with that scope. As a matter of fact I could not get any hotkey to work with a global scope. Can anyone else duplicate this action?

I’ve tried the “global” option several times in the past without success. I had put it down to my ailing iMac but clearly it doesn’t work.

Thanks for testing this Michael. I have filed a Bitbucket bug report as issue #840.

I tried this but all I get is “No previous versions available”. Since the problem appeared I have made many copies and worked on a new copy each time. Maybe restoring previous versions only works if it is the same file and not a copy. I’ll try staying on the same file from now on.

Thank you very much. I’ll try this which will certainly make things easier.

To help easily maintain the basckup folder created by the earlier hotkey, here is a companion procedure that can be run from the same file that is creating the backups. This removes all backup files older than the number of days you enter in the gettext dialog:

local theDatabase,backupList,backupLimit,removedFiles
backupLimit="10"
gettext "Enter number of days to retain backup files…",backupLimit
backupLimit=val(striptonum(backupLimit))*86400
backupList=listfiles(dbfolder()+info("databasename")+" Backup")
If error
    alertok info("error")
    stop
endif
looparray backupList,¶,element,index
    if filesuperdate(dbfolder()+info("databasename")+" Backup/"+element)<supernow()-backupLimit
        fileerase dbfolder()+info("databasename")+" Backup/"+element
endif
endloop
backupList=arraydifference(backupList,listfiles(dbfolder()+info("databasename")+" Backup"),¶)
backupLimit=backupLimit/86400
removedFiles=arraysize(backupList,¶)
If backupList=""
    message "No backups older than "+backupLimit+" days have been found!"
else
    message pattern(removedFiles,"# backup file~ older than "+backupLimit+" days ")+
        ?(removedFiles>1,"have","has")+" been removed!"
endif

The number of days set to retain backup files is multiplied by 86400 to determine that length in seconds for use later. It then builds a list of the current contents of the backup folder for this database. If there is no such folder it will display that error message and stop. Otherwise it uses that list in a looparray to see if each file in the list is older than the number of days you indicated. If so, the file is erased from the folder. The original list of files is then compared to the current list to see how many files have been removed and that number is assigned to the removedFiles variable for use in a following message. Also the backupLimit variable is reconverted back to days for use in the message that will show the number of removed files (if any were found).

This procedure could be run as often as needed or could be included in the .Initialize procedure with the gettext line removed and the backupLimit variable set to whatever the number of days interval you think appropriate. This would run the procedure every time the file is opened and automatically keep the contents of your backup folder limited to the number of days you have set.

One final thought is to add a save statement to the original hotkeys code so that pressing option-B would create a new backup file and save the current file all at the same time.

Thank you again for your help. Using these procedures will certainly make me feel safer. I don’t know if these database corruptions are frequent, but they really are a pain.

A useful improvement to your file backup name convention might be …

definehotkeys “database”,“control-B”,{makefolder dbfolder()+info(“databasename”)+" Backup"
saveacopyas info(“databasename”)+" Backup/"+info(“databasename”)+" "+superdatepattern(supernow(),"YYYY\MM\DD @ ",“HH;MM”)}

… in order to force chronological file name listings in Open Dialog boxes.

Yes, it’s easier to work with dates like that (and it removes the usual ambiguities). All my dates (and times when useful) are formatted this way: yyyy-mm-ddThh-mm-ss. This is close to the ISO format with “:” replaced by “-” for Finder friendlyness.

So, upon further reflection I thought that it might be nice to incorporate not only the creation of the backup files in the hotkey but include the backup folder maintenance in the hotkey code as well. This variation will do the following every time the control-B key is pressed:

  • Save the database
  • Create a backup folder if one does not already exist
  • Create a new current backup file of the database
  • Check to see how many files are in the backup folder
  • Remove any older files in the list over 15

I have also incorporated Gerald’s pattern suggestion into the file names but also assured proper chronological order by temporarily appending the creation superdate to the beginning of each backup file name when processing. Rather than the scheme of removing files older than x number of days I have instead opted to simply maintain the last 15 backups in the backup folder. You could certainly adjust this in the code to maintain as many as you feel comfortable with. Here is my suggestion for the new complete backup hotkey:

definehotkeys "database","Control-B",{save
    makefolder dbfolder()+info("databasename")+" Backup"
    saveacopyas info("databasename")+" Backup/"+info("databasename")+
        " "+superdatepattern(supernow(),"YYYY\MM\DD @ ","HH;MM")
local tempList,backupList
tempList=""
backupList=listfiles(dbfolder()+info("databasename")+" Backup")
looparray backupList,¶,element,index
    tempList=tempList+¶+str(filesuperdate(dbfolder()+info("databasename")+
        " Backup/"+element))+"•"+element
endloop
arraynumericsort arraystrip(tempList,¶),tempList,¶
backupList=arrayfilter(tempList,¶,|||array(import(),2,"•")|||)
If arraysize(backupList,¶)>15
    tempList=arrayrange(backupList,1,-16,¶)
    looparray tempList,¶,element,index
        fileerase dbfolder()+info("databasename")+" Backup/"+element
    endloop 
endif}

I think that you should use instead the date/time pattern “YYYY-MM-DDTHH-MM-SS” because the upper case MM and DD forces leading zeros to be included for the month and day numbers … or it did in Pan 6 … which is important if you want the date for day 3 to come before day 10 in the same month and year. Unfortunately upper case HH-MM-SS will not force leading zeros to be included for hours, minutes and seconds.

I agree. — For now you have to do the math yourself:

?(val(timepattern(now(),"hh"))<10,"0"+timepattern(now(),"hh"),timepattern(now(),"hh"))+":"+?(val(timepattern(now(),"mm"))<10,"0"+timepattern(now(),"mm"),timepattern(now(),"mm"))

gives the desired time pattern with leading zeros.

It isn’t actually necessary to test to see if the minutes are less than 10. “mm” always gives you 2 digits. It’s only “hh” that will give you 1 digit if it is less than 10. The way you are doing it, you will get 2 leading zeros, when the minutes are less than 10.

A simpler formula would be

("0"+timepattern(now(),"hh:mm")[-5,-1]

If the hour is already 2 digits long, the text funnel will trim off the unneeded leading zero.

The formula for this would be

datepattern(today(),"YYYY-MM-DD")+"T"+("0"+timepattern(now(),"hh-mm-ss"))[-8,-1]