Backing up projects

When I lost my iPad, I also lost all of my projects. Is there a way to backup projects onto another device or in the cloud?

@glwhart - there are a number of ways to back up. Using an external filing app or internally running the Codea backup facility from the main Codea system window. Also @dave1707 has written a number of project and project library backup projects. You can save each file as a zip file from the system backup options. Just check it out before you program further. You obviously need a storage option such as Dropbox to store them in.

If you lost your iPad, there’s not much you can do. If the iPad is brocken, you could still try to get it’s memory back.

@Bri_G I have dropbox and google drive so I could backup to one of those. Being somewhat new to Codea, I’m not sure how to do what you suggest. Could you offer step by step instructions for one of your proposed solutions? For example, I don’t know what you mean by “the main Codea system window”.

The easiest way is to hold on a project, three options will pop out. Select Copy Project Code and post it on pastebin/github, and on the other iPad run the installer.

local linkRawCode = "INSERT LINK TO WHERE YOUR CODE IS"

local function success(data)
    if data:find("%-%-%# Main") then --paste-into-project format
        data = data.."--# " --add new file tag to end in order to capture last file
        for name, file in data:gmatch("(%w+)\
(.-)\
%-%-%# ") do --capture each name and file
            saveProjectTab(name, file)
            print("Saved", name)
        end
    else --put it all in one tab
        saveProjectTab("Main", data)
    end
    load(data)() --load the file
    setup() --run it
end

local function failure(error)
    print("Error", error)
end

http.request(linkRawCode, success, failure)

@glwhart - there have been many posts on using Dropbox for backup of files. I’ll try to cover this .On the root of Dropbox there is a folder called Apps (on mine) within that folder any external package (Codea in this case) can add a package root folder - so I have a Codea folder there. You can add any other folders/files in that folder. Most files there will be visible to Codea provided they have a recognised file type. Files in folders will not be visible but can still be accessed if you use an absolute address.

Codea keeps a duplicate copy of visible files in the Codea root on your iPad. This is accessible via the Asserts option from the top left button on the Codea main window. When you change the Dropbox folder contents (remote) you need to sync the iPad Dropbox folder from the top menu in the Codea root folder.

Try it out and if you have problems we can guide you through it.

I know it’s a bit outdated, but still, the old wiki tells, that, “[a]t the moment, there is not an elegant answer to copying code to and from Codea.”. But you can try doing all the options presented above (or below) this post.
Personally I was fine with storing all the code in a hub like pastebin, and then just re-downloading the code this way.

Here’s my latest backup/restore program. It created a backup file of my 578 projects in less than a second. Once you create a backup file, you can restore all or selected projects. This saves the backup file in the Codea Dropbox folder where you can then sync it with the Dropbox app. When I got a new iPad, I synced the new iPad with the Dropbox app to get the backup file into the Codea Dropbox folder on the new iPad. I then emailed a copy of the backup project, which should be saved separately, from my old iPad to the new one. I selected restore all projects and unselected the backup project name. You can’t restore a running project or you’ll get a Codea error and the restore will stop. It took about a second or so to create my 578 Codea projects on the new iPad.

When the backup is run, it creates a backup file named bkAyymmdd-hhmm where yymmdd is the year, month, day and hhmm is the hour and minute that the file was created. You can do backups as often as you want as long as you wait at least a minute.

This only saves the Codea projects. If you have assets or images or anything other than the project, those aren’t saved.

I’ve used various versions of this code to save and restore projects thru the years. The only time it crashed was when I tried to restore the backup project which you can’t do. You can’t restore a running project. I did that just to see what would happen.

displayMode(FULLSCREEN)

function setup()
    delay=5
    pns="PROJ".."NAME".."START" -- project name start
    pne="PROJ".."NAME".."END"   -- project name end
    tns="TAB".."NAME".."START"  -- tab name start
    tne="TAB".."NAME".."END"    -- tab name end
    te="TAB".."END"             -- tab end
    pe="PROJ".."END"            -- project end
    bk1,bk2,bk3,bk4="","","",""
    msg1=""
    m1=button(WIDTH/2,HEIGHT-100,200,50,"Create a backup",createBkup)
    m2=button(WIDTH/2,HEIGHT-200,200,50,"Select a backup",getBkups)
    m3=button(WIDTH/2,HEIGHT-300,200,50,"Exit",exit)
    b1=button(190,HEIGHT-200,180,50,"Turn all projects on.",allOn)
    b2=button(190,HEIGHT-260,180,50,"Turn all projects off.",allOff)
    b3=button(190,HEIGHT-360,180,50,"Allow overwrite. OFF",allowOverwrite)
    b5=button(190,HEIGHT-520,180,50,"Restore selected items",restore)
    b6=button(190,100,180,50,"Return to menu",retn)
    func=menu
end

function draw()
    background(40, 40, 50)
    func()
end

function touched(t)
    if t.state==ENDED then
        if func==menu then
            m1:touched(t)
            m2:touched(t)
            m3:touched(t)
        elseif func==showBkups then
            for a,b in pairs(bkupTab) do
                if math.abs(t.x-WIDTH/2)<100 and math.abs(t.y-(HEIGHT-a*75+dy))<25 then
                    selectedBackupFile=b
                    func=getProjects
                end
            end
            b6:touched(t)
        elseif func==showProjects then
            for a,b in pairs(projTab) do
                if math.abs(t.x-WIDTH/2)<100 and math.abs(t.y-(HEIGHT-a*75+dy))<25 then
                    b.sel=not b.sel
                end
            end
            b1:touched(t)
            b2:touched(t)
            b3:touched(t)
            b5:touched(t)
            b6:touched(t)
        elseif func==showRestoreTable then
            b6:touched(t)
        end
    end
    if t.state==MOVING then
        if func==showProjects or func==showRestoreTable then
            if t.x<100 or t.x>WIDTH-100 then
                sp=1
                if t.y<HEIGHT/2 then
                    sp=20
                end
                dy=dy+t.deltaY*sp
                if dy<0 then
                    dy=0
                end
            end
        end
    end
end

function menu()
    fill(255)
    m1:draw()
    m2:draw()
    m3:draw()
    text(bk1,WIDTH/2,300)
    text(bk2,WIDTH/2,260)
    text(bk3,WIDTH/2,220)
    text(bk4,WIDTH/2,180)
end

function exit()
    close()
end

function retn()
    msg1=""
    func=menu
end

function createBkup()
    if delay>0 then
        func=createBkup
        text("Creating backup",WIDTH/2,HEIGHT/2)
        delay=delay-1
        return
    end
    local proj=listProjects("Documents")
    local t=os.date("*t")
    backupName=string.format("bkA%02d%02d%02d-%02d%02d",
    t.year%100,t.month,t.day,t.hour,t.min)
    local temp={}
    local dt=os.date()
    local cnt=0
    table.insert(temp,"BKUP==START.."..dt)
    for a,b in pairs(proj) do
        lst=listProjectTabs(b)
        cnt=cnt+1
        table.insert(temp,pns.."=="..b.."=="..pne)
        for c,d in pairs(lst) do
            table.insert(temp,tns.."=="..d.."=="..tne)
            table.insert(temp,readProjectTab(b..":"..d))
            table.insert(temp,"=="..te)
        end
        table.insert(temp,"=="..pe)
    end
    table.insert(temp,"BKUP==END")
    local str=table.concat(temp,"\
")
    bk1=backupName
    bk2=dt
    bk3="Projects saved = "..cnt
    bk4="File size = "..#str.." bytes"
    saveText("Dropbox:"..backupName,str)
    delay=5
    func=menu
end

function getBkups()
    bk1,bk2,bk3,bk4="","","",""
    bkupTab={}
    showBackupTable=true
    dy=0
    local lst=assetList("Dropbox")
    table.sort(lst)
    for a,b in pairs(lst) do
        if string.find(b,"bk") then
            table.insert(bkupTab,b)
        end
    end
    func=showBkups
end

function showBkups()
    for a,b in pairs(bkupTab) do
        sprite("Cargo Bot:Dialogue Button",WIDTH/2,HEIGHT-a*75+dy,200,50)
        text(b,WIDTH/2,HEIGHT-a*75+dy)
    end
    b6:draw()
end

function getProjects()
    text("Please wait, getting a list of all projects",WIDTH/2,HEIGHT/2)
    projTab={}
    local name=selectedBackupFile
    bkupFile=readText("Dropbox:"..name)
    if bkupFile==nil then
        alert("No backup file selected.","ERROR")
        return
    end
    local sss=pns.."==(.-)=="..pne
    for projName,proj in string.gmatch(bkupFile,sss) do
        table.insert(projTab,{proj=projName,sel=false,res=""})
    end
    dy=0
    func=showProjects
end

function showProjects()
    text("Tap a project to select or unselect it.",WIDTH/2,HEIGHT-20+dy)
    for a,b in pairs(projTab) do
        tint(255)
        if b.sel then
            tint(0, 255, 0, 255)
        end
        sprite("Cargo Bot:Dialogue Button",WIDTH/2,HEIGHT-a*75+dy,200,50)
        fill(255)
        text(b.proj,WIDTH/2,HEIGHT-a*75+dy)
        if b.res~="" then
            sprite("Cargo Bot:Dialogue Button",575,HEIGHT-a*75+dy,150,50)
            text(b.res,575,HEIGHT-a*75+dy)
        end
    end
    tint(255)
    b1:draw()
    b2:draw()
    b3:draw()
    b5:draw()
    b6:draw()
    scrollArea()
    text(msg1,200,200)
end

function scrollArea()
    fill(97, 229, 165, 100)
    rect(0,HEIGHT/2+20,100,HEIGHT)
    rect(0,0,100,HEIGHT/2-20)
    rect(WIDTH-100,HEIGHT/2+20,100,HEIGHT)
    rect(WIDTH-100,0,100,HEIGHT/2-20)
    fill(255)
    text("SLOW\
SCROLL\
AREA",50,HEIGHT*.75)
    text("FAST\
SCROLL\
AREA",50,HEIGHT*.25)
    text("SLOW\
SCROLL\
AREA",WIDTH-50,HEIGHT*.75)
    text("FAST\
SCROLL\
AREA",WIDTH-50,HEIGHT*.25)
end

function allOn()
    for a,b in pairs(projTab) do
        b.sel=true
    end
end

function allOff()
    for a,b in pairs(projTab) do
        b.sel=falss
    end
end

function allowOverwrite(self)
    Allow_overwrite=not Allow_overwrite
    if Allow_overwrite then
        self.name="Allow overwrite.  ON"
        self.col=color(255,0,0)
    else
        self.name="Allow overwrite.  OFF"
        self.col=color(255)
    end
end

function restore()
    resTab={}
    msg1=""
    if projTab==nil or selectedBackupFile=="" then
        alert("No projects selected.","ERROR")
        return
    end
    dy=0
    cnt=0
    for a,b in pairs(projTab) do
        if b.sel then
            cnt=cnt+1
            pname=b.proj
            restoreProject()
        end
    end
    if cnt>0 then
        alert("Check for error messages.","RESTORE COMPLETE")
    else
        alert("Nothing selected for restore.","ERROR")
        return
    end
    allOff()
    if Allow_overwrite then
        allowOverwrite(b3)
    end
    dy=0
    func=showRestoreTable
end

function restoreProject()
    name=pname
    local sss=pns.."==(.-)=="..pne.."\
(.-)=="..pe
    if hasProject(name) then
        if Allow_overwrite then
            deleteProject(name)
        else
            table.insert(resTab,{proj=name,res="Overwrite   OFF"})
            return
        end
    end
    if not hasProject(name) then
        createProject(name)
        for projName,proj in string.gmatch(bkupFile,sss) do
            if name==projName then
                getTab(proj)
            end
        end
        table.insert(resTab,{proj=name,res="Restored"})
    end
end

function getTab(proj)
    local sss=tns.."==(%g+)=="..tne.."\
(.-)\
=="..te
    for tabName,tab in string.gmatch(proj,sss) do
        saveProjectTab(name..":"..tabName,tab)
    end
end

function showRestoreTable()
    for a,b in pairs(resTab) do
        sprite("Cargo Bot:Dialogue Button",400,HEIGHT-a*75+dy,200,50)
        fill(255)
        text(b.proj,400,HEIGHT-a*75+dy)
        sprite("Cargo Bot:Dialogue Button",575,HEIGHT-a*75+dy,150,50)
        text(b.res,575,HEIGHT-a*75+dy)
    end
    b6:draw()
    scrollArea()
end

button=class()

function button:init(x,y,w,h,n,f)
    self.x=x
    self.y=y
    self.w=w
    self.h=h
    self.name=n
    self.func=f
    self.col=color(255)
end

function button:draw()
    sprite("Cargo Bot:Dialogue Button",self.x,self.y,self.w,self.h)
    fill(self.col)
    text(self.name,self.x,self.y)
end

function button:touched(t)
    if math.abs(t.x-self.x)<self.w/2 and math.abs(t.y-self.y)<self.h/2 then
        self.func(self)
    end
end

@glwhart - I can vouch for the backup system that @dave1707 has developed. I have been using this for a few months. The options are improving all the time, the selected project menu that @Anatoly describes is very new and I have used several times for backing up individual files. I suggest you try them all to see which you prefer.

I’ve created a Wiki Article on this. Read in the link attached below.

https://codea.fandom.com/wiki/Installer_Code