64187cb8d0b32d1fdec9d90f326edf75750e185d
kent
  Wed Mar 28 11:23:22 2012 -0700
First cut of Django output for autoSql.
diff --git src/hg/autoSql/autoSql.c src/hg/autoSql/autoSql.c
index 7e753a0..88ba3af 100644
--- src/hg/autoSql/autoSql.c
+++ src/hg/autoSql/autoSql.c
@@ -9,56 +9,59 @@
  *
  * This file is copyright 2002-2005 Jim Kent, but license is hereby
  * granted for all use - public, private or commercial. */
 
 #include "common.h"
 #include "errabort.h"
 #include "linefile.h"
 #include "obscure.h"
 #include "dystring.h"
 #include "asParse.h"
 #include "options.h"
 
 
 boolean withNull = FALSE;
 boolean makeJson = FALSE;
+boolean makeDjango = FALSE;
 
 void usage()
 /* Explain usage and exit. */
 {
 errAbort("autoSql - create SQL and C code for permanently storing\n"
          "a structure in database and loading it back into memory\n"
 	 "based on a specification file\n"
 	 "usage:\n"
 	 "    autoSql specFile outRoot {optional: -dbLink -withNull -json} \n"
 	 "This will create outRoot.sql outRoot.c and outRoot.h based\n"
 	 "on the contents of specFile. \n"
          "\n"
          "options:\n"
          "  -dbLink - optionally generates code to execute queries and\n"
          "            updates of the table.\n"
 	 "  -withNull - optionally generates code and .sql to enable\n"
          "              applications to accept and load data into objects\n"
 	 "              with potential 'missing data' (NULL in SQL)\n"
          "              situations.\n"
-	 "  -json - generate method to output the object in JSON format.\n");
+         "  -django - generate method to output object as django model Python code\n"
+	 "  -json - generate method to output the object in JSON (JavaScript) format.\n");
 }
 
 static struct optionSpec optionSpecs[] = {
     {"dbLink", OPTION_BOOLEAN},
     {"withNull", OPTION_BOOLEAN},
     {"json", OPTION_BOOLEAN},
+    {"django", OPTION_BOOLEAN},
     {NULL, 0}
 };
 
 void sqlColumn(struct asColumn *col, FILE *f)
 /* Print out column in SQL. */
 {
 fprintf(f, "    %s ", col->name);
 struct dyString *type = asColumnToSqlType(col);
 fprintf(f, "%s", type->string);
 if (!withNull)
     fprintf(f, " not null");
 fputc(',', f);
 fprintf(f, "\t# %s\n", col->comment);
 dyStringFree(&type);
 }
@@ -66,30 +69,91 @@
 void sqlTable(struct asObject *table, FILE *f)
 /* Print out structure of table in SQL. */
 {
 struct asColumn *col;
 
 fprintf(f, "\n#%s\n", table->comment);
 fprintf(f, "CREATE TABLE %s (\n", table->name);
 for (col = table->columnList; col != NULL; col = col->next)
     sqlColumn(col, f);
 
 fprintf(f,"              #Indices\n");
 fprintf(f, "    PRIMARY KEY(%s)\n", table->columnList->name);
 fprintf(f, ");\n");
 }
 
+void djangoEnumChoices(struct asColumn *col, FILE *f)
+/* Write out a list of choices to use with a django enum. */
+{
+fprintf(f, "    %sChoices = (\n", col->name);
+struct slName *val;
+for (val = col->values; val != NULL; val = val->next)
+    fprintf(f, "        ('%s', '%s'),\n", val->name, val->name);
+fprintf(f, "    )\n");
+}
+
+int longestValue(struct asColumn *col)
+/* Return length of longest value in col->values list */
+{
+int longest = 0;
+struct slName *val;
+for (val = col->values; val != NULL; val = val->next)
+    {
+    int len = strlen(val->name);
+    if (len > longest)
+        longest = len;
+    }
+return longest;
+}
+
+void djangoColumn(struct asColumn *col, FILE *f)
+/* Print out column in Django flavored python. */
+{
+fprintf(f, "    %s = models.", col->name);
+struct asTypeInfo *lt = col->lowType;
+if (lt->type == t_enum)
+    fprintf(f, "CharField(max_length=%d choices=%sChoices)",  longestValue(col), col->name);
+else if (lt->type == t_set)
+    {
+    warn("Set type fields such as '%s' are not tested in Django and are unlikely to work.",
+        col->name);
+    fprintf(f, "TextField() # A set in autoSql");
+    }
+else if (col->isList || col->isArray)
+    fprintf(f, "TextField()");
+else if (lt->type == t_char)
+    fprintf(f, "CharField(max_length=%d)", col->fixedSize ? col->fixedSize : 1);
+else if (lt->type == t_string)
+    fprintf(f, "CharField(max_length=255)");
+else
+    fprintf(f, "%s()", lt->djangoName);
+fprintf(f, "\t# %s\n", col->comment);
+}
+
+void djangoModel(struct asObject *table, FILE *f)
+/* Print out structure of table in Django flavored python */
+{
+fprintf(f, "class %s(models.Model):\n");
+struct asColumn *col;
+for (col = table->columnList; col != NULL; col = col->next)
+    if (col->lowType->type == t_enum)
+        djangoEnumChoices(col, f);
+for (col = table->columnList; col != NULL; col = col->next)
+    djangoColumn(col, f);
+fprintf(f, "\n");
+}
+
 static void cSymTypePrName(struct asObject *dbObj, char *name, FILE *f)
 /* print the C type name, prefixed with the object name */
 {
 char cname[1024];
 safef(cname, sizeof(cname), "%s%c%s", dbObj->name, toupper(name[0]), name+1);
 // Fix name so that it is a valid C identifier.
 char *c;
 for (c = cname; *c != '\0'; c++)
     {
     if (!(isalnum(*c) || (*c == '_')))
         *c = '_';
     }
 fputs(cname, f);
 }
 
@@ -1639,189 +1703,244 @@
    "/* Print out %s as a line in a tab-separated file. */\n\n", tableName);
 
 fprintf(hFile, 
    "#define %sCommaOut(el,f) %sOutput(el,f,',',',');\n", tableName, tableName);
 fprintf(hFile, 
    "/* Print out %s as a comma separated list including final comma. */\n\n", 
    	tableName);
 
 fprintf(f, "{\n");
 for (col = table->columnList; col != NULL; col = col->next)
     makeColOutput(col, f);
 fprintf(f, "}\n\n");
 }
 
 void makeJsonOutput(struct asObject *table, FILE *f, FILE *hFile)
-/* Make function that prints table to tab delimited file. */
+/* Make function that prints table in JSON format. */
 {
 char *tableName = table->name;
 struct asColumn *col;
 
 fprintf(hFile,
   "void %sJsonOutput(struct %s *el, FILE *f);\n", tableName, tableName);
 fprintf(hFile, "/* Print out %s in JSON format. */\n\n", tableName);
 
 fprintf(f,
   "void %sJsonOutput(struct %s *el, FILE *f) \n", tableName, tableName);
 fprintf(f, "/* Print out %s in JSON format. */\n", tableName);
 
 fprintf(f, "{\n");
 fprintf(f, "fputc('{',f);\n");
 for (col = table->columnList; col != NULL; col = col->next)
     {
     makeColJsonOutput(col, f);
     if (col->next != NULL)
         fprintf(f, "fputc(',',f);\n");
     }
 fprintf(f, "fputc('}',f);\n");
 fprintf(f, "}\n\n");
 }
 
+void makeDjangoOutput(struct asObject *table, FILE *f, FILE *hFile)
+/* Make function that prints table as django model in Python code */
+{
+char *tableName = table->name;
+struct asColumn *col;
+
+fprintf(hFile,
+  "void %sDjangoOutput(struct %s *el, FILE *f);\n", tableName, tableName);
+fprintf(hFile, "/* Print out %s as Django model in python. */\n\n", tableName);
+
+fprintf(f,
+  "void %sDjangoOutput(struct %s *el, FILE *f) \n", tableName, tableName);
+fprintf(f, "/* Print out %s as Django model in python. */\n\n", tableName);
+
+fprintf(f, "{\n");
+fprintf(f, "fputc('{',f);\n");
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    fprintf(f, "/* TBD %s */\n", col->name);
+    // makeColJsonOutput(col, f);
+    if (col->next != NULL)
+        fprintf(f, "fputc(',',f);\n");
+    }
+fprintf(f, "fputc('}',f);\n");
+fprintf(f, "}\n\n");
+}
 
 void cSymColumnDef(struct asColumn *col, FILE *cFile)
 /* output definition used for parsing and formating a symbolic column field */
 {
 struct slName *val;
 fprintf(cFile, "/* definitions for %s column */\n", col->name);
 fprintf(cFile, "static char *values_%s[] = {", col->name);
 for (val = col->values; val != NULL; val = val->next)
     fprintf(cFile, "\"%s\", ", val->name);
 fprintf(cFile, "NULL};\n");
 fprintf(cFile, "static struct hash *valhash_%s = NULL;\n\n", col->name);
 }
 
 void cSymColumnDefs(struct asObject *obj, FILE *cFile)
 /* output definitions used for parsing and formating a symbolic column fields */
 {
 struct asColumn *col;
 for (col = obj->columnList; col != NULL; col = col->next)
     {
     if ((col->lowType->type == t_enum) || (col->lowType->type == t_set))
         cSymColumnDef(col, cFile);
     }
 }
 
 void genObjectCode(struct asObject *obj, boolean doDbLoadAndSave,
-                   FILE *cFile, FILE *hFile, FILE *sqlFile)
+                   FILE *cFile, FILE *hFile, FILE *sqlFile, FILE *djangoFile)
 /* output code for one object */
 {
 cTable(obj, hFile);
 cSymColumnDefs(obj, cFile);
 
 if (obj->isTable)
     {
     sqlTable(obj, sqlFile);
+    if (makeDjango)
+        djangoModel(obj, djangoFile);
     if (!objectHasVariableLists(obj) && !objectHasSubObjects(obj))
         staticLoadRow(obj, cFile, hFile);
     if(doDbLoadAndSave)
         {
         dynamicLoadByQuery(obj, cFile, hFile);
         dynamicSaveToDb(obj, cFile, hFile);
         dynamicSaveToDbEscaped(obj, cFile, hFile);
         }
     }
 dynamicLoadRow(obj, cFile, hFile);
 dynamicLoadAll(obj, cFile, hFile);
 dynamicLoadAllByChar(obj, cFile, hFile);
 makeCommaIn(obj, cFile, hFile);
 /*if (makeJson)
     makeJsonInput(obj, cFile, hFile);*/
 
 if (obj->isSimple)
     {
     if (internalsNeedFree(obj))
 	makeFreeInternals(obj, cFile, hFile);
     }
 else
     {
     makeFree(obj, cFile, hFile);
     makeFreeList(obj, cFile, hFile);
     }
 makeOutput(obj, cFile, hFile);
 if (makeJson)
     makeJsonOutput(obj, cFile, hFile);
-printf("Made %s object\n", obj->name);
+verbose(2, "Made %s object\n", obj->name);
 }
 
+
 int main(int argc, char *argv[])
 {
 struct asObject *objList, *obj;
 char *outRoot, outTail[256];
 char dotC[256];
 char dotH[256];
 char dotSql[256];
+char dotDjango[256];
 FILE *cFile;
 FILE *hFile;
 FILE *sqlFile;
+FILE *djangoFile = NULL;
 char defineName[256];
 boolean doDbLoadAndSave = FALSE;
 
 optionInit(&argc, argv, optionSpecs);
 doDbLoadAndSave = optionExists("dbLink");
 withNull = optionExists("withNull");
 makeJson = optionExists("json");
+makeDjango = optionExists("django");
 
 if (argc != 3)
     usage();
 
 objList = asParseFile(argv[1]);
 outRoot = argv[2];
 /* don't embed directories in files */
 splitPath(outRoot, NULL, outTail, NULL);
 
-sprintf(dotC, "%s.c", outRoot);
+safef(dotC, sizeof(dotC), "%s.c", outRoot);
 cFile = mustOpen(dotC, "w");
-sprintf(dotH, "%s.h", outRoot);
+safef(dotH, sizeof(dotH), "%s.h", outRoot);
 hFile = mustOpen(dotH, "w");
-sprintf(dotSql, "%s.sql", outRoot);
+safef(dotSql, sizeof(dotSql), "%s.sql", outRoot);
 sqlFile = mustOpen(dotSql, "w");
+if (makeDjango)
+    {
+    safef(dotDjango, sizeof(dotDjango), "%s.django", outRoot);
+    djangoFile = mustOpen(dotDjango, "w");
+    }
 
 /* Print header comment in all files. */
 fprintf(hFile, 
    "/* %s.h was originally generated by the autoSql program, which also \n"
    " * generated %s.c and %s.sql.  This header links the database and\n"
    " * the RAM representation of objects. */\n\n",
    outTail, outTail, outTail);
 fprintf(cFile, 
    "/* %s.c was originally generated by the autoSql program, which also \n"
    " * generated %s.h and %s.sql.  This module links the database and\n"
    " * the RAM representation of objects. */\n\n",
    outTail, outTail, outTail);
 fprintf(sqlFile, 
    "# %s.sql was originally generated by the autoSql program, which also \n"
    "# generated %s.c and %s.h.  This creates the database representation of\n"
    "# an object which can be loaded and saved from RAM in a fairly \n"
    "# automatic way.\n",
    outTail, outTail, outTail);
+if (makeDjango)
+     {
+     fprintf(djangoFile, 
+         "# %s.python was originally generated by the autoSql program, which also \n"
+         "# generated %s.sql %s.c and %s.h.  This creates the database representation of\n"
+         "# an object which can be loaded and saved from RAM in a fairly \n"
+         "# automatic way.\n\n",
+         outTail, outTail, outTail, outTail);
+     }
 
 /* Bracket H file with definition that keeps it from being included twice. */
 sprintf(defineName, "%s_H", outTail);
 touppers(defineName);
 fprintf(hFile, "#ifndef %s\n", defineName);
 fprintf(hFile, "#define %s\n\n", defineName);
 if(doDbLoadAndSave)
     {
     fprintf(hFile, "#include \"jksql.h\"\n");
     }
 
 /* Put the usual includes in .c file, and also include .h file we are
  * generating. */
 fprintf(cFile, "#include \"common.h\"\n");
 fprintf(cFile, "#include \"linefile.h\"\n");
 fprintf(cFile, "#include \"dystring.h\"\n");
 fprintf(cFile, "#include \"jksql.h\"\n");
 fprintf(cFile, "#include \"%s\"\n", dotH);
 fprintf(cFile, "\n");
 fprintf(cFile, "\n");
 
+/* Also generate imports for django. */
+if (makeDjango)
+    {
+    fprintf(djangoFile, "import datetime\n");
+    fprintf(djangoFile, "from django.db import models\n\n");
+    }
+
 /* Process each object in specification file and output to .c, 
- * .h, and .sql. */
+ * .h, and .sql and even Django/Python. */
 for (obj = objList; obj != NULL; obj = obj->next)
-    genObjectCode(obj, doDbLoadAndSave, cFile, hFile, sqlFile);
+    genObjectCode(obj, doDbLoadAndSave, cFile, hFile, sqlFile, djangoFile);
 
 fprintf(cFile, "/* -------------------------------- End autoSql Generated Code -------------------------------- */\n\n");
 fprintf(hFile, "/* -------------------------------- End autoSql Generated Code -------------------------------- */\n\n");
+if (makeDjango)
+    fprintf(djangoFile, "###################### End autoSql Generated Code ######################\n\n");
 /* Finish off H file bracket. */
 fprintf(hFile, "#endif /* %s */\n\n", defineName);
 return 0;
 }