Commit bbd8f84e authored by platypro's avatar platypro

Reorganized how exercises are layed out in the file system. Also added exercise docs in JSON form.

parent 8659eaef
......@@ -38,10 +38,8 @@ add_executable(quizgrinder ${SRCS})
install(TARGETS quizgrinder DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
install(DIRECTORY
res/exercise
res/script
res/pset
res/template
../content/exercise
../content/pset
DESTINATION ${APP_DATA_PATH})
install(DIRECTORY DESTINATION ${APP_VAR_PATH})
......
......@@ -18,6 +18,7 @@
#include <alloca.h>
#include <ctype.h>
#include <math.h>
#include <errno.h>
#include <pmustache/template.h>
#include <pmustache/provider.wje.h>
......@@ -27,42 +28,12 @@
#include "script.h"
#include "keys.h"
char* genPath(char* path, char* dir)
char* exercise_loadResource(char* basepath, char* file)
{
size_t len = strlen(path) + strlen(dir) + 2;
char* result = malloc(len);
snprintf(result, len, "%s/%s", path, dir);
return result;
}
char* loadId(char* path, char* id, uint8_t chop)
{
size_t len;
char* result = NULL;
DIR *d;
struct dirent *dir;
len = strlen(id);
d = opendir(path);
while((dir = readdir(d)))
{
if((strlen(dir->d_name) - chop) != len) continue;
if(dir->d_type == DT_REG && !memcmp(dir->d_name, id, len))
{
result = dir->d_name;
break;
}
}
if(result)
{
result = genPath(path, result);
}
closedir(d);
return result;
size_t fpath_len = strlen(basepath) + strlen(file) + 2;
char* fpath = malloc(fpath_len);
snprintf(fpath, fpath_len, "%s/%s", basepath, file);
return fpath;
}
EXERCISE* mkExercise(PG_STATE* state)
......@@ -116,6 +87,7 @@ char* exercise_load(PG_STATE* state, char* filename)
state->exercise.problemSets = pset;
FILE* f = fopen(filename, "r");
if(!f) return NULL;
WJElement question = NULL;
WJReader pset_rdr =
......@@ -150,26 +122,36 @@ char* exercise_load(PG_STATE* state, char* filename)
{
if(!strcmp(exerFind->id, exerId))
{
exer=exerFind;
exer=exerFind;
break;
}
exerFind = exerFind->next;
}
if(!exer)
{
char* exerPath = loadId(state->path_exercise, exerId, 5);
{
char* exerPath = malloc(strlen(state->path_exercise) + strlen(exerId) + 1);
if(!exerPath) { return NULL; }
FILE* f = fopen(exerPath, "r");
free(exerPath);
if(!f) { return NULL; }
sprintf(exerPath, "%s/%s", state->path_exercise, exerId);
char* file = exercise_loadResource(exerPath, "exercise.json");
FILE* f = fopen(file, "r");
if(!f)
{
free(exerPath);
printf("File %s not found, aborting.\n", file);
free(file);
return NULL;
}
free(file);
WJReader exer_rdr =
WJROpenFILEDocument(f, NULL, 0);
//Load the exercise
exer = mkExercise(state);
exer->basepath = exerPath;
exer->staticQuestion.eref = exer;
char* obj = WJRNext(NULL, 15, exer_rdr);
......@@ -237,7 +219,7 @@ char* exercise_load(PG_STATE* state, char* filename)
if(exer->staticType != STATTYPE_STATIC)
{
char* scriptPath = loadId(state->path_script, exerId, 4);
char* scriptPath = exercise_loadResource(exerPath, "script.lua");
// script_load frees scriptPath
if(!scriptPath || !script_load(state, exerId, scriptPath))
{ WJRCloseDocument(exer_rdr); return NULL; }
......@@ -284,7 +266,7 @@ bool exercise_loadAll(PG_STATE* state)
{
if(dir->d_type == DT_REG)
{
char* fpath = genPath(state->path_pset, dir->d_name);
char* fpath = exercise_loadResource(state->path_pset, dir->d_name);
if(!fpath) return false;
exercise_load(state, fpath);
free(fpath);
......@@ -296,7 +278,7 @@ bool exercise_loadAll(PG_STATE* state)
char* exercise_loadOne(PG_STATE* state, char* set)
{
char* path = loadId(state->path_pset, set, 5);
char* path = exercise_loadResource(state->path_pset, set);
if(!path) return false;
set = exercise_load(state, path);
free(path);
......@@ -459,9 +441,10 @@ bool expandTemplate(struct PG_State* state, QUESTION* q, EXERTEMPLATE* temp, WJW
tc = calloc(1, sizeof(TEMPLATECONTAINER));
if(!tc) return false;
size_t l = strlen(APP_TEMPLATE_PATH) + strlen(q->eref->id) + strlen(temp->src) + 3;
size_t l = strlen(q->eref->basepath) + strlen(temp->src) + 3;
char* filename = alloca(l);
snprintf(filename, l, "%s/%s.%s", APP_TEMPLATE_PATH, q->eref->id, temp->src);
snprintf(filename, l, "%s/t%s", q->eref->basepath, temp->src);
tc->src = mustache_eatFile(filename);
if(!tc->src)
......
......@@ -57,6 +57,7 @@ typedef struct Exercise
char* calculator;
char* name;
char* basepath;
STATICTYPE staticType;
char* id;
......
......@@ -2,6 +2,4 @@
#define APP_IPC_PATH "@APP_VAR_PATH@"
#define APP_PROBSET_PATH APP_DATA_PATH "/pset"
#define APP_SCRIPT_PATH APP_DATA_PATH "/script"
#define APP_EXERCISE_PATH APP_DATA_PATH "/exercise"
#define APP_TEMPLATE_PATH APP_DATA_PATH "/template"
......@@ -82,9 +82,6 @@ error_t arg_parser (int key, char *arg, struct argp_state *s)
case OPT_PATH_PS:
state->path_pset = arg;
break;
case OPT_PATH_SC:
state->path_script = arg;
break;
case OPT_DAEMON_HTTP:
state->daemon.type |= DAEMON_HTTP;
break;
......@@ -112,7 +109,6 @@ error_t arg_parser (int key, char *arg, struct argp_state *s)
struct argp_option args[] = {
{"exercise-path", OPT_PATH_EX, "PATH", 0, "Path to find exercises", 0},
{"pset-path" , OPT_PATH_PS, "PATH", 0, "Path to find problem sets", 0},
{"script-path" , OPT_PATH_SC, "PATH", 0, "Path to find scripts", 0},
{NULL, 0, NULL , OPTION_DOC, "Daemons: These override any file operations", 0},
{"http-daemon" , OPT_DAEMON_HTTP, NULL, 0, "Run HTTP daemon. Overrides any file options.", 0},
{NULL, 0, NULL , OPTION_DOC, "File operations", 0},
......@@ -146,7 +142,6 @@ int main(int argc, char* argv[])
gstate = &state;
state.path_exercise = APP_EXERCISE_PATH;
state.path_script = APP_SCRIPT_PATH;
state.path_pset = APP_PROBSET_PATH;
state.gen_type = GTYPE_JSON;
state.gen_num = 10;
......
......@@ -26,8 +26,7 @@ typedef struct PG_State
PG_SCRIPT script;
PG_DAEMON daemon;
PG_HTTP http;
char* path_script;
char* path_exercise;
char* path_pset;
......
{
"name": "arith",
"description": "Generates arithmetic (+/-/m/d) problems. The max_1 and max_2 options define the constraints (add:max_1 + max_2 = x) (sub:max_1 - max_2 = x) (mul:max_1 * max_2 = x) (div: (max_1 * max_2) / max_2 = max_1)",
"options": [
{
"name": "operation",
"description": "The operation to generate questions for.",
"default": "add",
"type": "string",
"enum": ["add","sub","mul","div"]
},
{
"name": "max_1",
"description": "First max value (see description)",
"default": 20,
"type": "integer"
},
{
"name": "max_2",
"description": "Second max value (see description)",
"default": 20,
"type": "integer"
},
{
"name": "format",
"description": "The number type. Natural generates all integers. Real adds 2 decimal places to each generated value.",
"default": "natural",
"type": "string",
"enum": ["real","natural"]
}]
}
{
"name": "ptable",
"description": "Generates a question based on the periodic table. This was built to demonstrate static data storage and templates.",
"options": [
{
"name": "missing",
"description": "The missing value from the periodic table.",
"default": "name",
"type": "string",
"enum": ["name", "number"]
}],
"templates": ["default.svg"]
}
{
"name": "test",
"description": "A test exercise containing many widgets",
"options": []
}
......@@ -2,7 +2,7 @@
"name" : "Arithmetic",
"questions": [
{
"id":"arith",
"id":"net.platypro.arith",
"options" : {
"operation" : "add",
"max_1" : 100,
......@@ -10,7 +10,7 @@
}
},
{
"id":"arith",
"id":"net.platypro.arith",
"options" : {
"operation" : "sub",
"max_1" : 100,
......@@ -18,7 +18,7 @@
}
},
{
"id":"arith",
"id":"net.platypro.arith",
"options" : {
"operation" : "mul",
"max_1" : 15,
......@@ -26,7 +26,7 @@
}
},
{
"id":"arith",
"id":"net.platypro.arith",
"options" : {
"operation" : "div",
"max_1" : 20,
......
......@@ -2,10 +2,10 @@
"name" : "The Elements",
"questions" : [
{
"id":"ptable",
"id":"net.platypro.ptable",
},
{
"id":"ptable",
"id":"net.platypro.ptable",
"options":
{
"missing":"name"
......
{
"name" : "Test set",
"questions": [{"id":"test"}]
"questions": [{"id":"net.platypro.test"}]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment