diff --git a/checkin_notes b/checkin_notes
index 9b04882259..a8beea462d 100755
--- a/checkin_notes
+++ b/checkin_notes
@@ -8840,3 +8840,15 @@ David 31 Dec 2003
validate.C
test/
testbase.py
+
+David 31 Dec 2003
+ - Added Python assimilator, courtesy of Stephen Pellicer
+ All the back-end programs could be implemented in Python;
+ CPU usage is a non-factor.
+ Some people might prefer this to C++.
+ No reason not to have both, as long as the logic stays in synch.
+
+ py/Boinc/
+ sched_messages.py
+ sched/
+ assimilator.py
diff --git a/doc/anonymous_platform.php b/doc/anonymous_platform.php
new file mode 100644
index 0000000000..8c97b3ca0b
--- /dev/null
+++ b/doc/anonymous_platform.php
@@ -0,0 +1,73 @@
+
+require_once("docutil.php");
+page_head("Compile-it-yourself BOINC");
+echo "
+BOINC applications, and the BOINC core client, are native-mode programs,
+so different versions are required for each platform
+(a 'platform' is the combination of an operating
+system and a processor type: e.g., Linux/IntelX86).
+BOINC provides two ways to deal with the problem
+of making programs available for different platforms.
+
+
The project-compiles-it-for-you model
+
+In this approach, the BOINC-based project
+compiles program versions for common platforms
+(Windows/Intel, Linux/Intel, Mac OS/X. etc.),
+and places them on its servers.
+A participant downloads the core client for his platform
+(assuming that it's supported by the project).
+When the core client requests work from the project's scheduling server,
+it tells the server its platform,
+and the scheduling server instructs it to download
+the appropriate application executables.
+
+Although this addresses the needs of most BOINC participants,
+there are two groups for whom it is inadequate:
+
+-
+People who, for security reasons,
+want to only run executables they have compiled themselves.
+
-
+People whose computers have platforms not supported by the project
+(projects are generally resource-limited and cannot support all platforms).
+
+
+The compile-it-yourself model
+
+In this model participants compile programs themselves
+rather than downloading them from the project.
+Here's how it works:
+
+
+-
+Download the source code for the BOINC core client,
+and for the project's applications,
+and compile them for your computer.
+Alternatively, you might download the programs from
+a server of your choosing.
+
-
+Run the core client with a option telling
+it where to find the applications;
+it incorporates them
+into its state file and directory structure,
+as if it had been downloaded from the server.
+
-
+When the core client requests work from the scheduling server,
+it reports its platform as 'anonymous',
+and provides a list of the applications it has.
+The server then just sends whatever work is available for those applications.
+
+
+This model is possible only with projects that make their application
+source code available.
+
+
+Note: the compile-it-yourself model is under development,
+and is not currently available.
+Participants with uncommon platforms can prepare by porting
+the current core client and applications to their platforms.
+
+";
+page_tail();
+?>
diff --git a/doc/client.php b/doc/client.php
index c9cd432878..b6000d5bb1 100644
--- a/doc/client.php
+++ b/doc/client.php
@@ -114,12 +114,9 @@ The work manager's menu items are as follows:
-Menu names and other text in the work manager are stored in
-a file called language.ini.
-The release uses American English.
-Other languages are available
-here
-(thanks to Robi Buechler and other volunteers for this).
+The text displayed by the work manager (menu names, etc.)
+can be translated to languages other than English;
+information about this is here.
The BOINC screensaver can be selected using the Display Properties dialog.
diff --git a/doc/contact.php b/doc/contact.php
index 2bed3a5510..5350831528 100644
--- a/doc/contact.php
+++ b/doc/contact.php
@@ -29,6 +29,8 @@ Janus Kristensen
Tim Lan
+Stephen Pellicer
+
Rom Walton
Oliver Wang
diff --git a/doc/db_dump.php b/doc/db_dump.php
index bfd1ebab53..53939c3990 100644
--- a/doc/db_dump.php
+++ b/doc/db_dump.php
@@ -6,6 +6,13 @@ echo "
BOINC projects may export data describing teams, users and hosts.
This data is exported in XML files that can be downloaded via HTTP.
+
+This data can be summarized and represented as Web pages.
+An example (implemented using PHP) is at
+http://www.boinc.dk.
+If you are interested in using or contributing to this code,
+please contact the developer,
+Janus Kristensen.
The data is presented in several different 'views':
diff --git a/doc/participate.php b/doc/participate.php
index bdec622839..fbfa2ac5be 100644
--- a/doc/participate.php
+++ b/doc/participate.php
@@ -13,6 +13,8 @@ echo "
Computation credit
Teams
Downloading statistics data
+ Language customization
+ Compile-it-yourself BOINC
";
page_tail();
diff --git a/doc/translations.php b/doc/translations.php
index 28a6ad42dc..b07cd17d0c 100644
--- a/doc/translations.php
+++ b/doc/translations.php
@@ -5,45 +5,27 @@ echo "
Information about BOINC is available in the following languages:
If you'd like to add a web site to this list, please
contact us.
diff --git a/py/Boinc/sched_messages.py b/py/Boinc/sched_messages.py
new file mode 100644
index 0000000000..0931660ea8
--- /dev/null
+++ b/py/Boinc/sched_messages.py
@@ -0,0 +1,25 @@
+import sys, time
+
+CRITICAL=0
+NORMAL=1
+DEBUG=2
+
+class SchedMessages:
+ def __init__(self):
+ self.debug_level = 0
+
+ def set_debug_level(self, level):
+ self.debug_level = int(level)
+
+ def printf(self, kind, format, *args):
+ if kind <= self.debug_level:
+ if kind==CRITICAL:
+ kind = "CRITICAL"
+ elif kind==NORMAL:
+ kind = "normal "
+ elif kind==DEBUG:
+ kind = "debug "
+ else:
+ kind = "*** internal error: invalid MessageKind ***";
+ sys.stderr.write("%s [%s] " % (time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()), kind))
+ sys.stderr.write(format % args)
diff --git a/sched/assimilator.py b/sched/assimilator.py
new file mode 100644
index 0000000000..685dcf4f08
--- /dev/null
+++ b/sched/assimilator.py
@@ -0,0 +1,135 @@
+#!/usr/bin/python
+'''
+Python implementation of an assimilator
+Contributed by Stephen Pellicer
+'''
+
+import os, re, boinc_path_config, signal, sys, time
+from Boinc import database, boinc_db, configxml, sched_messages
+
+STOP_TRIGGER_FILENAME = os.path.join('..', 'stop_servers')
+caught_sig_int = False
+log_messages=sched_messages.SchedMessages()
+
+def check_stop_trigger():
+ global caught_sig_int, log_messages
+ try:
+ junk = open(STOP_TRIGGER_FILENAME, 'r')
+ except IOError:
+ if caught_sig_int:
+ log_messages.printf(sched_messages.CRITICAL, "Caught SIGINT\n")
+ sys.exit(1)
+ else:
+ log_messages.printf(sched_messages.CRITICAL, "Found stop trigger\n")
+ sys.exit(1)
+
+def sigint_handler(sig, stack):
+ global caught_sig_int
+ log_messages.printf(sched_messages.DEBUG, "Handled SIGINT\n")
+ caught_sig_int = True
+
+def get_file_path(result):
+ return os.path.join(config.upload_dir, re.search('(.*)', result.xml_doc_out).group(1))
+
+def assimilate_handler(wu, results, canonical_result):
+ # check for valid wu.canonical_resultid
+ if wu.canonical_result:
+ # do application specific processing
+ log_messages.printf(sched_messages.NORMAL, "[%s] Found canonical result\n", wu.name)
+ question = open(os.path.join('..', 'question'), 'r').read()[:32]
+ log_messages.printf(sched_messages.DEBUG, "Comparing to %s\n", question)
+ if len(question) != 32:
+ log_messages.printf(sched_messages.CRITICAL, "Question %s is wrong length\n", question)
+ else:
+ result = get_file_path(canonical_result)
+ for line in open(result, 'r').readlines():
+ line = line.strip()
+ log_messages.printf(sched_messages.DEBUG, " [%s] Answer found %s %s\n", canonical_result.name, line[-32:], line[:-33])
+ if line[-32:] == question:
+ log_messages.printf(sched_messages.CRITICAL, "[RESULT#%d %s] Found Answer %s\n", canonical_result.id, canonical_result.name, line[:-33])
+ else:
+ log_messages.printf(sched_messages.NORMAL, "[%s] No canonical result\n", wu.name)
+
+ if wu.error_mask&boinc_db.WU_ERROR_COULDNT_SEND_RESULT:
+ log_messages.printf(sched_messages.CRITICAL, "[%s] Error: couldn't send a result\n", wu.name)
+ if wu.error_mask&boinc_db.WU_ERROR_TOO_MANY_ERROR_RESULTS:
+ log_messages.printf(sched_messages.CRITICAL, "[%s] Error: too many error results\n", wu.name)
+ if wu.error_mask&boinc_db.WU_ERROR_TOO_MANY_TOTAL_RESULTS:
+ log_messages.printf(sched_messages.CRITICAL, "[%s] Error: too many total results\n", wu.name)
+ if wu.error_mask&boinc_db.WU_ERROR_TOO_MANY_SUCCESS_RESULTS:
+ log_messages.printf(sched_messages.CRITICAL, "[%s] Error: too many success results\n", wu.name)
+ # check for error conditions
+
+def do_pass(app):
+ did_something=False
+ # check for stop trigger
+ check_stop_trigger()
+
+ # look for workunits with correct appid and assimilate_state==ASSIMILATE_READY
+ for wu in database.Workunits.find(app=app, assimilate_state=boinc_db.ASSIMILATE_READY):
+ did_something=True
+ canonical_result=None
+ results=None
+ log_messages.printf(sched_messages.DEBUG, "[%s] assimilating: state=%d\n", wu.name, wu.assimilate_state)
+ results = database.Results.find(workunit=wu)
+ # look for canonical result for workunit in results
+ for result in results:
+ if result == wu.canonical_result:
+ canonical_result=result
+
+ # assimilate handler
+ assimilate_handler(wu, results, canonical_result)
+
+ # tag wu as ASSIMILATE_DONE
+ wu.assimilate_state = boinc_db.ASSIMILATE_DONE
+ wu.transition_time = int(time.time())
+ wu.commit()
+
+ # set wu transition_time
+
+ # return did something result
+ return did_something
+
+# main function
+asynch = False
+one_pass = False
+appname = ''
+
+# check for asynch one_pass, debug, app
+args = sys.argv[1:]
+args.reverse()
+while(len(args)):
+ arg = args.pop()
+ if arg == '-asynch':
+ asynch = True
+ elif arg == '-one_pass':
+ one_pass = True
+ elif arg == '-d':
+ arg = args.pop()
+ log_messages.set_debug_level(arg)
+ elif arg == '-app':
+ arg = args.pop()
+ appname = arg
+ else:
+ log_messages.printf(sched_messages.CRITICAL, "Unrecognized arg: %s\n", arg)
+
+config = configxml.default_config().config
+database.connect()
+
+# fork if asynch
+if(asynch):
+ # add fork code
+ pass
+
+# retrieve app where name = app.name
+app=database.Apps.find1(name=appname)
+signal.signal(signal.SIGINT, sigint_handler)
+
+# do one pass or execute main loop
+if one_pass:
+ do_pass(app)
+else:
+ # main loop
+ while(1):
+ if not do_pass(app):
+ time.sleep(10)