atomic append to google spreadsheet by gspread - google-sheets-api

The append in the sense of not trivially using append_row in which it will write at the end of the spreadsheet itself but at the end of contents that potentially could be appended by other users (from google drive for example).
Is it possible? Does gspread support say lock the file (or lock certain rows to be non-editable). I scanned through their doc but didn't find any interface seems relevant.
If not, are there any library that could do it?

You can use Class Protection. A protected range can protect either a static range of cells or a named range. It may include unprotected regions. The canEdit method determines whether the user has permission to edit the protected range of sheets. You may also use getEditors were in you get the list of editors for the protected range or sheet. Throws an exception if the user does not have permission to edit the protected range or sheet.

Related

How to check the CURRENTLY OPEN excel file is open in read only mode?

OPENING: I've been digging around for a long time looking at some of the answers to the questions here. I hope my Google-fu isn't getting rusty but I couldn't find anything helpful. I've tried what I have found with no luck.
PROBLEM: I have a document that is used by more then one user. The short vesion is the document ends up in Read Only mode one way or another and they forget its Read Only (or they just press the Okay button and dont read the warning). You can still operate everything in the worksheet as normal.
Through normal operation of the worksheet and the Macros the Workbook will auto save on completion of the Macro.
Is there any way to check (with the worksheet open) that the version you have is open in Read Only mode? I'd like to introduce code to prevent the use of the main function (disable the workbook macro) while using it in Read Only.
IDEAL SOLUTION: Have a function I could call to check the Read Only Status of the workbook.
Example:
Public Function ReadOnlyMode () as Bool
'Code that I don't know how to write for the funtion
return True/False
End Function
Use the following to test
If ActiveWorkbook.ReadOnly Then 'If True
See .ReadOnly property description.
Workbook.ReadOnly Property (Excel)
Returns True if the object has been opened as read-only. Read-only
Boolean .

Formulas to be triggered when Excel is opened or saved

I have several custom-made (VBA) formulas in my Excel. Examples are
= isfileExists(c3)
= isReadOnly(c3)
There are multiple calls to these functions (200/column)
These are slowing down the file as they involve heavy VBA. I want the above formulas to be run only when user opens the file or saves the file only!
How can I go on to do this without touching 'enabling/disabling automatic calculations'? (Please note I do know how to write the event functions but I am looking for an idea to put inside them/anywhere)
Sample idea I have is, adding a ' infront the formulas and whenever user opens/saves, that ' (apostrophe) would be removed using macro; hence the formulas would calculate. Any other easier suggestions?
Thanks much!
You can cache the formula results in a Global (or Static inside the function) dictionary object: once you've checked a file once you can use the cached result instead of repeating the file check)
Public Function ExpensiveCheck(fpath As String) As Boolean
Static dict As Object, ans As Boolean
'create the dictionary if not already created
If dict Is Nothing Then
Set dict = CreateObject("scripting.dictionary")
End If
If Not dict.exists(fpath) Then
ans = (Dir(fpath, vbNormal) <> "") 'do your checking here
dict.Add fpath, ans
End If
ExpensiveCheck = dict(fpath)
End Function
The dict will be lost when the session ends, so the formula should always refresh the cache when you open the file.
Downside is you may need some mechanism to clear/refresh the cache if you want to update the output after some change in the files being checked.
If you don't want your UDFs to calculate along with the rest of the worksheet cells, then you don't want UDFs; make them Private, or stick Option Private Module at the top of the standard module where they're declared, to make them unavailable to use as custom worksheet functions.
Then handle Workbook.Open and Workbook.BeforeSave events, and have these two handlers call some Private Sub that's responsible for writing to the cells you currently have these UDFs in.
That way the values will only be computed when the workbook opens and before it's saved.
UDFs that incur disk I/O aren't ideal: keep that processing for macros.

VBA Excel add in -- Initialize global variables on load -- not in workbooks

I am creating an excel add-in that connects to functions and formulas set up on an API.
To use the API functions the user needs to log in and receive a Token.
The Token is a global variable that is referenced whenever calling an API function.
This add in will be used by many different workbooks so i'm trying to make it as easy as possible for the user to connect.
At the moment i have a function that connects and stores the details in global variable so they only have to log in once. But every time they close and re open they have to log back in by calling the function and passing in the details.
Is there a way of accessing initialize workbook from an add-in?
an on-load equivalent?
Or getting add-in Functions to run on start up? without the user having to set up macros or custom functions, they can just add in and auto connect with their details.
'Is their an equivalent of
Sub New() / Sub OnLoad()
Initalize()
End sub
I don't want the user to have to worry at all about whats going on behind they just have to load up the page with their username and password in defined cells or a function that references the username and password that even when the page is set to manual calculations it will recalculate on load.
Any help would be appreciated thanks.
Rather than storing the token as a global, store it as a defined name, which is persistent.
ThisWorkbook.Names.Add "Token", sToken
Then simply evaluate the name every time you need the token.
sToken = ThisWorkbook.Sheets(1).[Token]
If anyone was curious. I found that you can initialize the global variables with something similar to OnLoad().
Private WebAddress As String
Sub Auto_Open()
WebAddress = "www.stackoverflow.com"
End Sub
Can be in a module, and is called when the workbook opens.

Directly referencing userform entry in VBA instead of passing value to a variable

folks. I am new to programming, but I am writing some macros to help manage a shared Excel workbook for my job.
I am implementing a few different user roles for people who need to access this workbook. The security is not very critical, just to prevent people from accidentally making (and saving) changes to things they shouldn't be. I am just having a UserForm prompt for the password and, based on what's entered, grant the proper access.
I have it written so that the user's entry into textbox on the UserForm is referenced directly as Me.textboxPasswordEntry.Value for any comparisons. It occurs to me that this may not be best practice, but I can't put my finger on why. Maybe I'm just over thinking? At the same time, it seems silly and wasteful to declare a variable, pass the value to the variable, and then analyze that.
The Sub below is from the UserForm, and I've included it to show you what I mean. This is a very straight-forward scenario, I know, but am I courting trouble if I continue this practice through more complex ones? If so, what kind of problems might I run into?
Thanks.
Private Sub buttonOK_adminPW_Click()
'The subs SetUserType_[level] are in the ChangeUserType module
'AdminPass and DesignPass are module-level variables set on UserForm initialization
'Default user type is User. Read-only access.
'Admins can edit the workbook, but must save via a macro to ensure
' things are reset properly for Users (some sheets hidden, etc.)
'Designers can edit the workbook, but also have full ability to save using
' the regular file menu/ctrl+s/etc.
Application.ScreenUpdating = False
Select Case Me.textboxPasswordEntry.Value
Case AdminPass
'Shows right control buttons and unlocks the wkbk for admin access
SetUserType_admin
Unload Me
Case DesignPass
'Shows all control buttons and unlocks the wkbk for designer access
SetUserType_design
Unload Me
Case Else
MsgBox ("Password incorrect. Please retry.")
With Me.textboxPasswordEntry
.Value = ""
.SetFocus
End With
End Select
Application.ScreenUpdating = True
End Sub
Yeah I've also pondered over "best practise" with userforms over the years... I guess it's just through experience that I use approach below most often:
Use as little code as possible in the userform itself (thinking
is, the form is more "reusable" if it does as little as possible
back to its parent... its reason for existance is just to get input)
Do use code on the "activate" event of the form to clear all the
fields on the form (this makes sense to be in the form because then
you don't need to remember every control on the form to clear at
every point you use it)
Either directly reference objects from
the form in your calling code (i.e. stPassword =
userform1.tbPassword.value) or...
Use "public" variables in the
userform ... i.e. before all code in userform declare "public stPasswordInput as string" then you can reference in your calling code with e.g. stPassword = userform1.stPasswordInput
I'm keen to see what other people suggest though!

How to prevent VBA variables from being shared across Word documents?

I have a VBA template project that runs automatically when a Word document is opened. However, if I open multiple documents, they all share the variables values. How can declare these variables to be only associated with the active window or active document?
I tried declaring them in a Class Module, but that did not help. Switching between opened document I can see that these variables are shared.
Any input is appreciated...
This what I have in my Module:
Option Private Module
Dim CurrentCommand As String
Public Function SetCurrentCommand(command)
CurrentCommand = command
End Function
Public Function GetCurrentCommand()
GetCurrentCommand = CurrentCommand
End Function
More Info: The code/Macro start at AutoExec like this:
Public Sub Main()
Set oAppClass.oApp = Word.Application
If PollingRate <> "" Then Application.OnTime Now + TimeValue(PollingRate), "CaptureUserViewState"
End Sub
And the CaptureUserViewState is a Sub that resides in a different Module and does all teh checks (comparing new values to last recorded ones) and here how this Sub does the check:
If WL_GetterAndSetter.GetLastPageVerticalPercentage <> pageVerticalPercentScrolled Then
'Update the last value variable
WL_GetterAndSetter.SetLastPageVerticalPercentage (pageVerticalPercentScrolled)
'log change
End If
You don't give us much information, but I assume you declared public variables at module level like this:
Public myString As String
Public myDouble As Double
From VBA documentation:
Variables declared using the Public statement are available to all procedures in all modules in all applications unless Option Private Module is in effect; in which case, the variables are public only within the project in which they reside.
The answer is to use Option Private Module.
When used in host applications that allow references across multiple projects, Option Private Module prevents a module’s contents from being referenced outside its project.
[...] If used, the Option Private statement must appear at module level, before any procedures.
EDIT You have now clarified that you declare your variables using Dim at module level. In this case, Option Private Module is irrelevant.
Variables declared with Dim at the module level are available to all procedures within the module.
i.e. regardless of whether you're using Option Private Module or not.
If you're finding that the values are retained between runs, then that must be because you are running a procedure from the same module from the same workbook. You may think you're doing something else, but in reality this is what you're doing.
EDIT
In your class module, instead of Dim CurrentCommand As String try Private CurrentCommand As String. Without more information it's hard to debug your program. I'm just taking random potshots here.
What you need to do is store multiple versions of the variables, one set per document.
So I would suggest that you create a simple class to hold the different values.
You then store them in a collection mapping the data-set with the document name or similar as the key.
In classmodule (MyData), marked as public:
Public data1 as String
Public data2 as Integer
In module with the event-handlers:
Dim c as new Collection 'module global declaration
Sub AddData()
Dim d as new MyData 'Your data set
d.data1 = "Some value"
d.data2 = 42
c.add Value:=d, Key:=ActiveDocument.name
End Sub
Then when you enter the event-handler you retrieve the data and use the specific set for the currently active document.
Sub EventHandler()
Dim d as MyData
set d = c.item(ActiveDocument.name)
'use data
'd.data1...
End Sub
Please not that this code is just on conceptual level. It is not working, You have to apply it to your problem but it should give you some idea on what you need to do. You will need to add alot of error handling, checking if the item is already in the collection and so on, but I hope you understand the concept to continue trying on your own.
The reason for this is because, as I understand the situation from your question, you only have one version of your script running, but multiple documents. Hence the script have to know about all the different documents.
On the other hand, If each document would have their own code/eventhandlers, hence having multiple versions of the script running, then you don't need the solution provided above. Instead you need to be careful what document instance you reference in your script. By always using "ThisDocument" instead of "ActiveDocument" you could achieve isolation if the code is placed in each open document.
However, as I understood it, you only have one version of the script running, separate from the open documents, hence the first solution applies.
Best of luck!
You might want to store the Document Specific details using
The Document.CustomDocumentProperties Property
http://msdn.microsoft.com/en-us/library/office/aa212718(v=office.11).aspx
This returns a
DocumentProperties Collection
Which you can add new Properties to Using
Document.CustomDocumentProperties.Add(PropertyName, LinkToContent, Value, Type)
And then Read From using
Document.CustomDocumentProperties.Item(PropertyName)
A downside, or bonus, here is that the properties will remain stored in the document unless you delete them.
This may be a good thing or a bad thing