77a1a4c6ee6c882e187f1fc80b531c0e61f957cd
chmalee
  Tue Nov 19 15:23:59 2024 -0800
Be more intelligent about producing default hub names, don't die when a hub exists already and we are adding another file to it

diff --git src/hg/lib/userdata.c src/hg/lib/userdata.c
index 5c68fc5..fdb9445 100644
--- src/hg/lib/userdata.c
+++ src/hg/lib/userdata.c
@@ -93,77 +93,94 @@
 
 void addHubSpaceRowForFile(struct hubSpace *row)
 /* We created a file for a user, now add an entry to the hubSpace table for it */
 {
 struct sqlConnection *conn = hConnectCentral();
 
 // now write out row to hubSpace table
 if (!sqlTableExistsOnMain(conn, "hubSpace"))
     {
     errAbort("No hubSpace MySQL table is present. Please send an email to genome-www@soe.ucsc.edu  describing the exact steps you took just before you got this error");
     }
 hubSpaceSaveToDb(conn, row, "hubSpace", 0);
 hDisconnectCentral(&conn);
 }
 
+static boolean checkHubSpaceRowExists(struct hubSpace *row)
+/* Return TRUE if row already exists */
+{
+struct sqlConnection *conn = hConnectCentral();
+struct dyString *queryCheck = sqlDyStringCreate("select count(*) from hubSpace where userName='%s' and fileName='%s' and parentDir='%s'", row->userName, row->fileName, row->parentDir);
+int ret = sqlQuickNum(conn, dyStringCannibalize(&queryCheck));
+hDisconnectCentral(&conn);
+return ret > 0;
+}
+
 static void makeParentDirRows(char *userName, time_t lastModified, char *db, char *parentDirStr)
 /* For each '/' separated component of parentDirStr, create a row in hubSpace. Return the 
  * final subdirectory component of parentDirStr */
 {
 int i, slashCount = countChars(parentDirStr, '/');
 char *components[256];
 struct dyString *currLocation = dyStringNew(0);
 int foundSlashes = chopByChar(cloneString(parentDirStr), '/', components, slashCount);
 if (foundSlashes > 256)
     errAbort("parentDir setting '%s' too long", parentDirStr);
 for (i = 0; i < foundSlashes; i++)
     {
     char *subdir = components[i];
     if (sameString(subdir, "."))
         continue;
     fprintf(stderr, "making row for parent dir: '%s'\n", subdir);
     if (!subdir)
         errAbort("error: empty subdirectory components for parentDir string '%s'", parentDirStr);
     struct hubSpace *row = NULL;
     AllocVar(row);
     row->userName = userName;
     row->fileName = subdir;
     row->fileSize = 0;
     row->fileType = subdir;
     row->creationTime = NULL;
     row->lastModified = sqlUnixTimeToDate(&lastModified, TRUE);
     row->db = db;
     row->location = cloneString(dyStringContents(currLocation));
     row->md5sum = "";
     row->parentDir = i > 0 ? components[i-1] : "";
+    // only insert a row for this parentDir if it's unique to the table
+    if (!checkHubSpaceRowExists(row))
         addHubSpaceRowForFile(row);
     dyStringAppendC(currLocation, '/');
     dyStringAppend(currLocation, components[i]);
     }
 }
 
 char *writeHubText(char *path, char *userName, char *hubName, char *db)
 /* Create a hub.txt file, optionally creating the directory holding it. For convenience, return
  * the file name of the created hub, which can be freed. */
 {
 int oldUmask = 00;
 oldUmask = umask(0);
 makeDirsOnPath(path);
 // restore umask
 umask(oldUmask);
 // now make the hub.txt with some basic information
-char *hubFile = catTwoStrings(path, "/hub.txt");
+char *hubFile = NULL;
+struct dyString *hubFileDy = dyStringCreate("%s%shub.txt", path, endsWith(path, "/") ? "" : "/");
+hubFile = dyStringCannibalize(&hubFileDy);
+if (fileExists(hubFile))
+    return hubFile;
+
 FILE *f = mustOpen(hubFile, "w");
 //fprintf(stderr, "would write \"hub %s\nemail %s\nshortLabel %s\nlongLabel %s\nuseOneFile on\n\ngenome %s\n\n\" to %s", hubName, emailForUserName(userName), hubName, hubName, db, hubFile);
 fprintf(f, "hub %s\n"
     "email %s\n"
     "shortLabel %s\n"
     "longLabel %s\n"
     "useOneFile on\n"
     "\n"
     "genome %s\n"
     "\n",
     hubName, emailForUserName(userName), hubName, hubName, db);
 carefulClose(&f);
 return hubFile;
 }
 
@@ -212,30 +229,31 @@
 row->fileSize = fileSize(hubFileName);
 row->fileType = "hub";
 row->creationTime = NULL;
 time_t lastModTime = fileModTime(hubFileName);
 row->lastModified = sqlUnixTimeToDate(&lastModTime, TRUE);
 row->db = db;
 row->location = path;
 row->md5sum = md5HexForFile(hubFileName);
 fprintf(stderr, "making parent dir rows\n");
 fflush(stderr);
 makeParentDirRows(userName, lastModTime, db, parentDir);
 row->parentDir = hubName;
 struct dyString *parentsPath = dyStringCreate("%s%s", getDataDir(userName), parentDir);
 fprintf(stderr, "parentDir of hub.txt: '%s'\n", parentsPath->string);
 fflush(stderr);
+if (!checkHubSpaceRowExists(row))
     addHubSpaceRowForFile(row);
 }
 
 static void deleteHubSpaceRow(char *fname)
 /* Deletes a row from the hubspace table for a given fname */
 {
 struct sqlConnection *conn = hConnectCentral();
 struct dyString *deleteQuery = sqlDyStringCreate("delete from hubSpace where location='%s'", fname);
 sqlUpdate(conn, dyStringCannibalize(&deleteQuery));
 }
 
 void removeFileForUser(char *fname, char *userName)
 /* Remove a file for this user if it exists */
 {
 // The file to remove must be prefixed by the hg.conf userDataDir
@@ -321,33 +339,66 @@
         struct userFiles *hubFileList = listFilesForUserHub(userName, hub->hubName);
         hub->lastModified = getFileListLatestTime(hubFileList);
         hub->fileList = hubFileList;
         slAddHead(&userHubs, hub);
         }
     }
 return userHubs;
 }
 
 struct hubSpace *listFilesForUser(char *userName)
 /* Return the files the user has uploaded */
 {
 struct sqlConnection *conn = hConnectCentral();
 struct dyString *query = sqlDyStringCreate("select userName, fileName, fileSize, fileType, creationTime, DATE_FORMAT(lastModified, '%%c/%%d/%%Y, %%l:%%i:%%s %%p') as lastModified, db, location, md5sum, parentDir from hubSpace where userName='%s' order by creationTime, fileName", userName);
 struct hubSpace *fileList = hubSpaceLoadByQuery(conn, dyStringCannibalize(&query));
+hDisconnectCentral(&conn);
 return fileList;
 }
 
+#define defaultHubName "defaultHub"
+char *defaultHubNameForUser(char *userName)
+/* Return a name to use as a default for a hub, starts with defaultHub, then defaultHub2, ... */
+{
+if (!userName)
+    return defaultHubName;
+struct dyString *query = sqlDyStringCreate("select distinct(fileName) from hubSpace where parentDir='' and fileName like '%s%%' and userName='%s'", defaultHubName, userName);
+struct sqlConnection *conn = hConnectCentral();
+struct slName *hubNames = sqlQuickList(conn, dyStringCannibalize(&query));;
+hDisconnectCentral(&conn);
+if (hubNames == NULL)
+    // user has no hubs created
+    return defaultHubName;
+slSort(&hubNames,slNameCmpStringsWithEmbeddedNumbers);
+slReverse(&hubNames);
+// now the first element of the list has the most recent integer to use (or no integer)
+char *currHubName = cloneString(hubNames->name);
+int currHubStrLen = strlen(currHubName);
+int defaultLen = strlen(defaultHubName);
+if (currHubStrLen == defaultLen)
+    // probably a common case
+    return "defaultHub2";
+else
+    {
+    currHubName[defaultLen-1] = 0;
+    currHubName += strlen(defaultHubName);
+    int hubNum = sqlUnsigned(currHubName) + 1;
+    struct dyString *hubName = dyStringCreate("%s%d", defaultHubName, hubNum);
+    return dyStringCannibalize(&hubName);
+    }
+}
+
 long long getMaxUserQuota(char *userName)
 /* Return how much space is allocated for this user or the default */
 {
 return HUB_SPACE_DEFAULT_QUOTA;
 }
 
 long long checkUserQuota(char *userName)
 /* Return the amount of space a user is currently using */
 {
 long long quota = 0;
 struct hubSpace *hubSpace, *hubSpaceList = listFilesForUser(userName);
 for (hubSpace = hubSpaceList; hubSpace != NULL; hubSpace = hubSpace->next)
     {
     quota += hubSpace->fileSize;
     }