liquid_feedback_core

changeset 394:326dc0c3b859

Calculation of "order_in_admission_state"
author jbe
date Fri Oct 11 12:57:03 2013 +0200 (2013-10-11)
parents 25fbce923cf2
children d93428e4edad
files Makefile lf_update_issue_order.c
line diff
     1.1 --- a/Makefile	Thu Oct 10 11:37:50 2013 +0200
     1.2 +++ b/Makefile	Fri Oct 11 12:57:03 2013 +0200
     1.3 @@ -1,4 +1,4 @@
     1.4 -all:: lf_update lf_update_suggestion_order
     1.5 +all:: lf_update lf_update_issue_order lf_update_suggestion_order
     1.6  
     1.7  lf_update: lf_update.c
     1.8  	cc	-Wall -O2 \
     1.9 @@ -6,6 +6,12 @@
    1.10  		-L "`pg_config --libdir`" \
    1.11  		-o lf_update lf_update.c -lpq
    1.12  
    1.13 +lf_update_issue_order: lf_update_issue_order.c
    1.14 +	cc	-Wall -O2 \
    1.15 +		-I "`pg_config --includedir`" \
    1.16 +		-L "`pg_config --libdir`" \
    1.17 +		-o lf_update_issue_order lf_update_issue_order.c -lpq
    1.18 +
    1.19  lf_update_suggestion_order: lf_update_suggestion_order.c
    1.20  	cc	-Wall -O2 \
    1.21  		-I "`pg_config --includedir`" \
    1.22 @@ -13,4 +19,4 @@
    1.23  		-o lf_update_suggestion_order lf_update_suggestion_order.c -lpq
    1.24  
    1.25  clean::
    1.26 -	rm -f lf_update lf_update_suggestion_order
    1.27 +	rm -f lf_update lf_update_issue_order lf_update_suggestion_order
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/lf_update_issue_order.c	Fri Oct 11 12:57:03 2013 +0200
     2.3 @@ -0,0 +1,507 @@
     2.4 +#include <stdlib.h>
     2.5 +#include <stdio.h>
     2.6 +#include <string.h>
     2.7 +#include <libpq-fe.h>
     2.8 +#include <search.h>
     2.9 +
    2.10 +static int logging = 0;
    2.11 +
    2.12 +static char *escapeLiteral(PGconn *conn, const char *str, size_t len) {
    2.13 +  // provides compatibility for PostgreSQL versions prior 9.0
    2.14 +  // in future: return PQescapeLiteral(conn, str, len);
    2.15 +  char *res;
    2.16 +  size_t res_len;
    2.17 +  res = malloc(2*len+3);
    2.18 +  if (!res) return NULL;
    2.19 +  res[0] = '\'';
    2.20 +  res_len = PQescapeStringConn(conn, res+1, str, len, NULL);
    2.21 +  res[res_len+1] = '\'';
    2.22 +  res[res_len+2] = 0;
    2.23 +  return res;
    2.24 +}
    2.25 +
    2.26 +static void freemem(void *ptr) {
    2.27 +  // to be used for "escapeLiteral" function
    2.28 +  // provides compatibility for PostgreSQL versions prior 9.0
    2.29 +  // in future: PQfreemem(ptr);
    2.30 +  free(ptr);
    2.31 +}
    2.32 +
    2.33 +// column numbers when querying "issue_supporter_in_admission_state" view in function main():
    2.34 +#define COL_MEMBER_ID 0
    2.35 +#define COL_WEIGHT    1
    2.36 +#define COL_ISSUE_ID  2
    2.37 +
    2.38 +// data structure for a candidate (in this case a suggestion) to the proportional runoff system:
    2.39 +struct candidate {
    2.40 +  char *key;              // identifier of the candidate, which is the "suggestion_id" string
    2.41 +  double score_per_step;  // added score per step
    2.42 +  double score;           // current score of candidate; a score of 1.0 is needed to survive a round
    2.43 +  int seat;               // equals 0 for unseated candidates, or contains rank number
    2.44 +};
    2.45 +
    2.46 +// compare two integers stored as strings (invocation like strcmp):
    2.47 +static int compare_id(char *id1, char *id2) {
    2.48 +  int ldiff;
    2.49 +  ldiff = strlen(id1) - strlen(id2);
    2.50 +  if (ldiff) return ldiff;
    2.51 +  else return strcmp(id1, id2);
    2.52 +}
    2.53 +
    2.54 +// compare two candidates by their key (invocation like strcmp):
    2.55 +static int compare_candidate(struct candidate *c1, struct candidate *c2) {
    2.56 +  return compare_id(c1->key, c2->key);
    2.57 +}
    2.58 +
    2.59 +// candidates are stored as global variables due to the constrained twalk() interface:
    2.60 +static int candidate_count;
    2.61 +static struct candidate *candidates;
    2.62 +
    2.63 +// function to be passed to twalk() to store candidates ordered in candidates[] array:
    2.64 +static void register_candidate(char **candidate_key, VISIT visit, int level) {
    2.65 +  if (visit == postorder || visit == leaf) {
    2.66 +    struct candidate *candidate;
    2.67 +    candidate = candidates + (candidate_count++);
    2.68 +    candidate->key  = *candidate_key;
    2.69 +    candidate->seat = 0;
    2.70 +    if (logging) printf("Candidate #%i is suggestion #%s.\n", candidate_count, candidate->key);
    2.71 +  }
    2.72 +}
    2.73 +
    2.74 +// performs a binary search in candidates[] array to lookup a candidate by its key (which is the suggestion_id):
    2.75 +static struct candidate *candidate_by_key(char *candidate_key) {
    2.76 +  struct candidate *candidate;
    2.77 +  struct candidate compare;
    2.78 +  compare.key = candidate_key;
    2.79 +  candidate = bsearch(&compare, candidates, candidate_count, sizeof(struct candidate), (void *)compare_candidate);
    2.80 +  if (!candidate) {
    2.81 +    fprintf(stderr, "Candidate not found (should not happen).\n");
    2.82 +    abort();
    2.83 +  }
    2.84 +  return candidate;
    2.85 +}
    2.86 +
    2.87 +// ballot of the proportional runoff system:
    2.88 +struct ballot {
    2.89 +  int weight;  // if weight is greater than 1, then the ballot is counted multiple times
    2.90 +  int count;
    2.91 +  struct candidate **candidates;
    2.92 +};
    2.93 +
    2.94 +// determine candidate, which is assigned the next seat (starting with the worst rank):
    2.95 +static struct candidate *loser(int round_number, struct ballot *ballots, int ballot_count) {
    2.96 +  int i, j;       // index variables for loops
    2.97 +  int remaining;  // remaining candidates to be seated
    2.98 +  // reset scores of all candidates:
    2.99 +  for (i=0; i<candidate_count; i++) {
   2.100 +    candidates[i].score = 0.0;
   2.101 +  }
   2.102 +  // calculate remaining candidates to be seated:
   2.103 +  remaining = candidate_count - round_number;
   2.104 +  // repeat following loop, as long as there is more than one remaining candidate:
   2.105 +  while (remaining > 1) {
   2.106 +    if (logging) printf("There are %i remaining candidates.\n", remaining);
   2.107 +    double scale;  // factor to be later multiplied with score_per_step:
   2.108 +    // reset score_per_step for all candidates:
   2.109 +    for (i=0; i<candidate_count; i++) {
   2.110 +      candidates[i].score_per_step = 0.0;
   2.111 +    }
   2.112 +    // calculate score_per_step for all candidates:
   2.113 +    for (i=0; i<ballot_count; i++) {
   2.114 +      int matches = 0;
   2.115 +      for (j=0; j<ballots[i].count; j++) {
   2.116 +        struct candidate *candidate;
   2.117 +        candidate = ballots[i].candidates[j];
   2.118 +        if (candidate->score < 1.0 && !candidate->seat) matches++;
   2.119 +      }
   2.120 +      if (matches) {
   2.121 +        double score_inc;
   2.122 +        score_inc = (double)ballots[i].weight / (double)matches;
   2.123 +        for (j=0; j<ballots[i].count; j++) {
   2.124 +          struct candidate *candidate;
   2.125 +          candidate = ballots[i].candidates[j];
   2.126 +          if (candidate->score < 1.0 && !candidate->seat) {
   2.127 +            candidate->score_per_step += score_inc;
   2.128 +          }
   2.129 +        }
   2.130 +      }
   2.131 +    }
   2.132 +    // calculate scale factor:
   2.133 +    scale = (double)0.0;  // 0.0 is used to indicate that there is no value yet
   2.134 +    for (i=0; i<candidate_count; i++) {
   2.135 +      double max_scale;
   2.136 +      if (candidates[i].score_per_step > 0.0) {
   2.137 +        max_scale = (1.0-candidates[i].score) / candidates[i].score_per_step;
   2.138 +        if (scale == 0.0 || max_scale <= scale) {
   2.139 +          scale = max_scale;
   2.140 +        }
   2.141 +      }
   2.142 +    }
   2.143 +    // add scale*score_per_step to each candidates score:
   2.144 +    for (i=0; i<candidate_count; i++) {
   2.145 +      int log_candidate = 0;
   2.146 +      if (logging && candidates[i].score < 1.0 && !candidates[i].seat) log_candidate = 1;
   2.147 +      if (log_candidate) printf("Score for suggestion #%s = %.4f+%.4f*%.4f", candidates[i].key, candidates[i].score, scale, candidates[i].score_per_step);
   2.148 +      if (candidates[i].score_per_step > 0.0) {
   2.149 +        double max_scale;
   2.150 +        max_scale = (1.0-candidates[i].score) / candidates[i].score_per_step;
   2.151 +        if (max_scale == scale) {
   2.152 +          // score of 1.0 should be reached, so we set score directly to avoid floating point errors:
   2.153 +          candidates[i].score = 1.0;
   2.154 +          remaining--;
   2.155 +        } else {
   2.156 +          candidates[i].score += scale * candidates[i].score_per_step;
   2.157 +          if (candidates[i].score >= 1.0) remaining--;
   2.158 +        }
   2.159 +      }
   2.160 +      if (log_candidate) {
   2.161 +        if (candidates[i].score >= 1.0) printf("=1\n");
   2.162 +        else printf("=%.4f\n", candidates[i].score);
   2.163 +      }
   2.164 +      // when there is only one candidate remaining, then break inner (and thus outer) loop:
   2.165 +      if (remaining <= 1) {
   2.166 +        break;
   2.167 +      }
   2.168 +    }
   2.169 +  }
   2.170 +  // return remaining candidate:
   2.171 +  for (i=0; i<candidate_count; i++) {
   2.172 +    if (candidates[i].score < 1.0 && !candidates[i].seat) return candidates+i;
   2.173 +  }
   2.174 +  // if there is no remaining candidate, then something went wrong:
   2.175 +  fprintf(stderr, "No remaining candidate (should not happen).");
   2.176 +  abort();
   2.177 +}
   2.178 +
   2.179 +// write results to database:
   2.180 +static int write_ranks(PGconn *db, char *escaped_area_id) {
   2.181 +  PGresult *res;
   2.182 +  char *cmd;
   2.183 +  int i;
   2.184 +  if (asprintf(&cmd, "BEGIN; UPDATE \"issue\" SET \"order_in_admission_state\" = NULL WHERE \"area_id\" = %s AND (\"state\" = 'admission' OR \"order_in_admission_state\" NOTNULL)", escaped_area_id) < 0) {
   2.185 +    fprintf(stderr, "Could not prepare query string in memory.\n");
   2.186 +    abort();
   2.187 +  }
   2.188 +  res = PQexec(db, cmd);
   2.189 +  free(cmd);
   2.190 +  if (!res) {
   2.191 +    fprintf(stderr, "Error in pqlib while sending SQL command to initiate issue update.\n");
   2.192 +    return 1;
   2.193 +  } else if (
   2.194 +    PQresultStatus(res) != PGRES_COMMAND_OK &&
   2.195 +    PQresultStatus(res) != PGRES_TUPLES_OK
   2.196 +  ) {
   2.197 +    fprintf(stderr, "Error while executing SQL command to initiate issue update:\n%s", PQresultErrorMessage(res));
   2.198 +    PQclear(res);
   2.199 +    return 1;
   2.200 +  } else {
   2.201 +    PQclear(res);
   2.202 +  }
   2.203 +  for (i=0; i<candidate_count; i++) {
   2.204 +    char *escaped_issue_id;
   2.205 +    escaped_issue_id = escapeLiteral(db, candidates[i].key, strlen(candidates[i].key));
   2.206 +    if (!escaped_issue_id) {
   2.207 +      fprintf(stderr, "Could not escape literal in memory.\n");
   2.208 +      abort();
   2.209 +    }
   2.210 +    if (asprintf(&cmd, "UPDATE \"issue\" SET \"order_in_admission_state\" = %i WHERE \"id\" = %s", candidates[i].seat, escaped_issue_id) < 0) {
   2.211 +      fprintf(stderr, "Could not prepare query string in memory.\n");
   2.212 +      abort();
   2.213 +    }
   2.214 +    freemem(escaped_issue_id);
   2.215 +    res = PQexec(db, cmd);
   2.216 +    free(cmd);
   2.217 +    if (!res) {
   2.218 +      fprintf(stderr, "Error in pqlib while sending SQL command to update issue order.\n");
   2.219 +    } else if (
   2.220 +      PQresultStatus(res) != PGRES_COMMAND_OK &&
   2.221 +      PQresultStatus(res) != PGRES_TUPLES_OK
   2.222 +    ) {
   2.223 +      fprintf(stderr, "Error while executing SQL command to update issue order:\n%s", PQresultErrorMessage(res));
   2.224 +      PQclear(res);
   2.225 +    } else {
   2.226 +      PQclear(res);
   2.227 +      continue;
   2.228 +    }
   2.229 +    res = PQexec(db, "ROLLBACK");
   2.230 +    if (res) PQclear(res);
   2.231 +    return 1;
   2.232 +  }
   2.233 +  res = PQexec(db, "COMMIT");
   2.234 +  if (!res) {
   2.235 +    fprintf(stderr, "Error in pqlib while sending SQL command to commit transaction.\n");
   2.236 +    return 1;
   2.237 +  } else if (
   2.238 +    PQresultStatus(res) != PGRES_COMMAND_OK &&
   2.239 +    PQresultStatus(res) != PGRES_TUPLES_OK
   2.240 +  ) {
   2.241 +    fprintf(stderr, "Error while executing SQL command to commit transaction:\n%s", PQresultErrorMessage(res));
   2.242 +    PQclear(res);
   2.243 +    return 1;
   2.244 +  } else {
   2.245 +    PQclear(res);
   2.246 +    return 0;
   2.247 +  }
   2.248 +}
   2.249 +
   2.250 +// calculate ordering of issues in admission state for an area and call write_ranks() to write it to database:
   2.251 +static int process_area(PGconn *db, PGresult *res, char *escaped_area_id) {
   2.252 +  int err;                 // variable to store an error condition (0 = success)
   2.253 +  int ballot_count = 1;    // number of ballots, must be initiatized to 1, due to loop below
   2.254 +  struct ballot *ballots;  // data structure containing the ballots
   2.255 +  int i;                   // index variable for loops
   2.256 +  // create candidates[] and ballots[] arrays:
   2.257 +  {
   2.258 +    void *candidate_tree = NULL;  // temporary structure to create a sorted unique list of all candidate keys
   2.259 +    int tuple_count;              // number of tuples returned from the database
   2.260 +    char *old_member_id = NULL;   // old member_id to be able to detect a new ballot in loops
   2.261 +    struct ballot *ballot;        // pointer to current ballot
   2.262 +    int candidates_in_ballot = 0; // number of candidates in ballot
   2.263 +    // reset candidate count:
   2.264 +    candidate_count = 0;
   2.265 +    // determine number of tuples:
   2.266 +    tuple_count = PQntuples(res);
   2.267 +    // trivial case, when there are no tuples:
   2.268 +    if (!tuple_count) {
   2.269 +      if (logging) printf("Nothing to do.\n");
   2.270 +      return 0;
   2.271 +    }
   2.272 +    // calculate ballot_count and generate set of candidate keys (suggestion_id is used as key):
   2.273 +    for (i=0; i<tuple_count; i++) {
   2.274 +      char *member_id, *issue_id;
   2.275 +      member_id = PQgetvalue(res, i, COL_MEMBER_ID);
   2.276 +      issue_id = PQgetvalue(res, i, COL_ISSUE_ID);
   2.277 +      if (!candidate_tree || !tfind(issue_id, &candidate_tree, (void *)compare_id)) {
   2.278 +        candidate_count++;
   2.279 +        if (!tsearch(issue_id, &candidate_tree, (void *)compare_id)) {
   2.280 +          fprintf(stderr, "Insufficient memory while inserting into candidate tree.\n");
   2.281 +          abort();
   2.282 +        }
   2.283 +      }
   2.284 +      if (old_member_id && strcmp(old_member_id, member_id)) ballot_count++;
   2.285 +      old_member_id = member_id;
   2.286 +    }
   2.287 +    // allocate memory for candidates[] array:
   2.288 +    candidates = malloc(candidate_count * sizeof(struct candidate));
   2.289 +    if (!candidates) {
   2.290 +      fprintf(stderr, "Insufficient memory while creating candidate list.\n");
   2.291 +      abort();
   2.292 +    }
   2.293 +    // transform tree of candidate keys into sorted array:
   2.294 +    candidate_count = 0;  // needed by register_candidate()
   2.295 +    twalk(candidate_tree, (void *)register_candidate);
   2.296 +    // free memory of tree structure (tdestroy() is not available on all platforms):
   2.297 +    while (candidate_tree) tdelete(*(void **)candidate_tree, &candidate_tree, (void *)compare_id);
   2.298 +    // allocate memory for ballots[] array:
   2.299 +    ballots = calloc(ballot_count, sizeof(struct ballot));
   2.300 +    if (!ballots) {
   2.301 +      fprintf(stderr, "Insufficient memory while creating ballot list.\n");
   2.302 +      abort();
   2.303 +    }
   2.304 +    // set ballot weights, determine ballot section sizes, and verify preference values:
   2.305 +    ballot = ballots;
   2.306 +    old_member_id = NULL;
   2.307 +    for (i=0; i<tuple_count; i++) {
   2.308 +      char *member_id;
   2.309 +      int weight;
   2.310 +      member_id = PQgetvalue(res, i, COL_MEMBER_ID);
   2.311 +      weight = (int)strtol(PQgetvalue(res, i, COL_WEIGHT), (char **)NULL, 10);
   2.312 +      if (weight <= 0) {
   2.313 +        fprintf(stderr, "Unexpected weight value.\n");
   2.314 +        free(ballots);
   2.315 +        free(candidates);
   2.316 +        return 1;
   2.317 +      }
   2.318 +      if (old_member_id && strcmp(old_member_id, member_id)) ballot++;
   2.319 +      ballot->weight = weight;
   2.320 +      ballot->count++;
   2.321 +      old_member_id = member_id;
   2.322 +    }
   2.323 +    // allocate memory for ballot sections:
   2.324 +    for (i=0; i<ballot_count; i++) {
   2.325 +      if (ballots[i].count) {
   2.326 +        ballots[i].candidates = malloc(ballots[i].count * sizeof(struct candidate *));
   2.327 +        if (!ballots[i].candidates) {
   2.328 +          fprintf(stderr, "Insufficient memory while creating ballot section.\n");
   2.329 +          abort();
   2.330 +        }
   2.331 +      }
   2.332 +    }
   2.333 +    // fill ballot sections with candidate references:
   2.334 +    old_member_id = NULL;
   2.335 +    ballot = ballots;
   2.336 +    for (i=0; i<tuple_count; i++) {
   2.337 +      char *member_id, *issue_id;
   2.338 +      member_id = PQgetvalue(res, i, COL_MEMBER_ID);
   2.339 +      issue_id = PQgetvalue(res, i, COL_ISSUE_ID);
   2.340 +      if (old_member_id && strcmp(old_member_id, member_id)) {
   2.341 +        ballot++;
   2.342 +        candidates_in_ballot = 0;
   2.343 +      }
   2.344 +      ballot->candidates[candidates_in_ballot++] = candidate_by_key(issue_id);
   2.345 +      old_member_id = member_id;
   2.346 +    }
   2.347 +    // print ballots, if logging is enabled:
   2.348 +    if (logging) {
   2.349 +      for (i=0; i<ballot_count; i++) {
   2.350 +        int j;
   2.351 +        printf("Ballot #%i: ", i+1);
   2.352 +        for (j=0; j<ballots[i].count; j++) {
   2.353 +          if (!j) printf("issues ");
   2.354 +          else printf(", ");
   2.355 +          printf("#%s", ballots[i].candidates[j]->key);
   2.356 +        }
   2.357 +        // if (!j) printf("empty");  // should not happen
   2.358 +        printf(".\n");
   2.359 +      }
   2.360 +    }
   2.361 +  }
   2.362 +
   2.363 +  // calculate ranks based on constructed data structures:
   2.364 +  for (i=0; i<candidate_count; i++) {
   2.365 +    struct candidate *candidate = loser(i, ballots, ballot_count);
   2.366 +    candidate->seat = candidate_count - i;
   2.367 +    if (logging) printf("Assigning rank #%i to issue #%s.\n", candidate_count-i, candidate->key);
   2.368 +  }
   2.369 +
   2.370 +  // free ballots[] array:
   2.371 +  for (i=0; i<ballot_count; i++) {
   2.372 +    // if (ballots[i].count) {  // count should not be zero
   2.373 +      free(ballots[i].candidates);
   2.374 +    // }
   2.375 +  }
   2.376 +  free(ballots);
   2.377 +
   2.378 +  // write results to database:
   2.379 +  if (logging) printf("Writing ranks to database.\n");
   2.380 +  err = write_ranks(db, escaped_area_id);
   2.381 +  if (logging) printf("Done.\n");
   2.382 +
   2.383 +  // free candidates[] array:
   2.384 +  free(candidates);
   2.385 +
   2.386 +  // return error code of write_ranks() call
   2.387 +  return err;
   2.388 +}
   2.389 +
   2.390 +int main(int argc, char **argv) {
   2.391 +
   2.392 +  // variable declarations:
   2.393 +  int err = 0;
   2.394 +  int i, count;
   2.395 +  char *conninfo;
   2.396 +  PGconn *db;
   2.397 +  PGresult *res;
   2.398 +
   2.399 +  // parse command line:
   2.400 +  if (argc == 0) return 1;
   2.401 +  if (argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
   2.402 +    FILE *out;
   2.403 +    out = argc == 1 ? stderr : stdout;
   2.404 +    fprintf(out, "\n");
   2.405 +    fprintf(out, "Usage: %s [-v|--verbose] <conninfo>\n", argv[0]);
   2.406 +    fprintf(out, "\n");
   2.407 +    fprintf(out, "<conninfo> is specified by PostgreSQL's libpq,\n");
   2.408 +    fprintf(out, "see http://www.postgresql.org/docs/9.1/static/libpq-connect.html\n");
   2.409 +    fprintf(out, "\n");
   2.410 +    fprintf(out, "Example: %s dbname=liquid_feedback\n", argv[0]);
   2.411 +    fprintf(out, "\n");
   2.412 +    return argc == 1 ? 1 : 0;
   2.413 +  }
   2.414 +  {
   2.415 +    size_t len = 0;
   2.416 +    int argb = 1;
   2.417 +    if (
   2.418 +      argc >= 2 &&
   2.419 +      (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--verbose"))
   2.420 +    ) {
   2.421 +      argb = 2;
   2.422 +      logging = 1;
   2.423 +    }
   2.424 +    for (i=argb; i<argc; i++) len += strlen(argv[i]) + 1;
   2.425 +    conninfo = malloc(len * sizeof(char));
   2.426 +    if (!conninfo) {
   2.427 +      fprintf(stderr, "Error: Could not allocate memory for conninfo string.\n");
   2.428 +      abort();
   2.429 +    }
   2.430 +    conninfo[0] = 0;
   2.431 +    for (i=argb; i<argc; i++) {
   2.432 +      if (i>argb) strcat(conninfo, " ");
   2.433 +      strcat(conninfo, argv[i]);
   2.434 +    }
   2.435 +  }
   2.436 +
   2.437 +  // connect to database:
   2.438 +  db = PQconnectdb(conninfo);
   2.439 +  if (!db) {
   2.440 +    fprintf(stderr, "Error: Could not create database handle.\n");
   2.441 +    return 1;
   2.442 +  }
   2.443 +  if (PQstatus(db) != CONNECTION_OK) {
   2.444 +    fprintf(stderr, "Could not open connection:\n%s", PQerrorMessage(db));
   2.445 +    return 1;
   2.446 +  }
   2.447 +
   2.448 +  // go through areas:
   2.449 +  res = PQexec(db, "SELECT \"id\" FROM \"area\"");
   2.450 +  if (!res) {
   2.451 +    fprintf(stderr, "Error in pqlib while sending SQL command selecting areas to process.\n");
   2.452 +    err = 1;
   2.453 +  } else if (PQresultStatus(res) != PGRES_TUPLES_OK) {
   2.454 +    fprintf(stderr, "Error while executing SQL command selecting areas to process:\n%s", PQresultErrorMessage(res));
   2.455 +    err = 1;
   2.456 +    PQclear(res);
   2.457 +  } else if (PQnfields(res) < 1) {
   2.458 +    fprintf(stderr, "Too few columns returned by SQL command selecting areas to process.\n");
   2.459 +    err = 1;
   2.460 +    PQclear(res);
   2.461 +  } else {
   2.462 +    count = PQntuples(res);
   2.463 +    if (logging) printf("Number of areas to process: %i\n", count);
   2.464 +    for (i=0; i<count; i++) {
   2.465 +      char *area_id, *escaped_area_id;
   2.466 +      char *cmd;
   2.467 +      PGresult *res2;
   2.468 +      area_id = PQgetvalue(res, i, 0);
   2.469 +      if (logging) printf("Processing area #%s:\n", area_id);
   2.470 +      escaped_area_id = escapeLiteral(db, area_id, strlen(area_id));
   2.471 +      if (!escaped_area_id) {
   2.472 +        fprintf(stderr, "Could not escape literal in memory.\n");
   2.473 +        abort();
   2.474 +      }
   2.475 +      if (asprintf(&cmd, "SELECT \"member_id\", \"weight\", \"issue_id\" FROM \"issue_supporter_in_admission_state\" WHERE \"area_id\" = %s ORDER BY \"member_id\"", escaped_area_id) < 0) {
   2.476 +        fprintf(stderr, "Could not prepare query string in memory.\n");
   2.477 +        abort();
   2.478 +      }
   2.479 +      res2 = PQexec(db, cmd);
   2.480 +      free(cmd);
   2.481 +      if (!res2) {
   2.482 +        fprintf(stderr, "Error in pqlib while sending SQL command selecting issue supporter in admission state.\n");
   2.483 +        err = 1;
   2.484 +      } else if (PQresultStatus(res2) != PGRES_TUPLES_OK) {
   2.485 +        fprintf(stderr, "Error while executing SQL command selecting issue supporter in admission state:\n%s", PQresultErrorMessage(res));
   2.486 +        err = 1;
   2.487 +        PQclear(res2);
   2.488 +      } else if (PQnfields(res2) < 3) {
   2.489 +        fprintf(stderr, "Too few columns returned by SQL command selecting issue supporter in admission state.\n");
   2.490 +        err = 1;
   2.491 +        PQclear(res2);
   2.492 +      } else {
   2.493 +        if (process_area(db, res2, escaped_area_id)) err = 1;
   2.494 +        PQclear(res2);
   2.495 +      }
   2.496 +      freemem(escaped_area_id);
   2.497 +    }
   2.498 +    PQclear(res);
   2.499 +  }
   2.500 +
   2.501 +  // cleanup and exit:
   2.502 +  PQfinish(db);
   2.503 +  if (!err) {
   2.504 +    if (logging) printf("Successfully terminated.\n");
   2.505 +  } else {
   2.506 +    fprintf(stderr, "Exiting with error code %i.\n", err);
   2.507 +  }
   2.508 +  return err;
   2.509 +
   2.510 +}

Impressum / About Us