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; }